React 类组件和函数组件
组件component
一.概念
Element VS Component (元素与组件)
不成文的约定:元素小写,组件大写
- const div=React.createElement('div',...)
- 这是一个React元素(小写)
- const Div=()=>React.createElement('div',...)
- 这是一个React组件(大写)
什么是组件?
能跟其他物件组合起来的物件,就是组件,组件并没有明确的定义。 就目前而言,一个返回值是React元素的函数就是组件
Vue里,data、methods、render
组合成的一个对象(构造选项)就可以表示一个组件
React两种组件
1.函数组件
function Welcome(props){ return <h1>Hello,{props.name}</h1> } //使用方法: <Welcome name="frank"/>
函数组件注意事项
2.类组件
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1> } } //使用方法: <Welcome name="frank"/>
<Welcome />
会被翻译成什么?
<div />
翻译为React.createElement('div')
div是元素
<Welcome />
翻译为React.createElement(Welcome)
Welcome是函数
类组件注意事项
小试牛刀
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> 爸爸 <Son /> </div> ); } class Son extends React.Component { constructor() { super(); this.state = { n: 0 }; } add() { // this.state.n += 1 为什么不行 this.setState({ n: this.state.n + 1 }); } render() { return ( <div className="Son"> 儿子 n: {this.state.n} <button onClick={() => this.add()}>+1</button> <Grandson /> </div> ); } } const Grandson = () => { const [n, setN] = React.useState(0); return ( <div className="Grandson"> 孙子 n:{n} <button onClick={() => setN(n + 1)}>+1</button> </div> ); }; const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
添加props
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> 爸爸 <Son messageForSon="儿子你好" /> </div> ); } class Son extends React.Component { render() { return ( <div className="Son"> 我是儿子,我爸对我说「{this.props.messageForSon}」 <Grandson messageForGrandson="孙贼你好" /> </div> ); } } const Grandson = props => { return ( <div className="Grandson"> 我是孙子,我爷对我说「{props.messageForGrandson}」 </div> ); }; const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
this.props.xxx
类组件用this.state
读,this.setState
写
外部数据props不能写,只能读。
添加state
import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { return ( <div className="App"> 爸爸 <Son /> </div> ); } class Son extends React.Component { constructor() { super(); this.state = { n: 0 }; } add() { // this.state.n += 1 为什么不行 this.setState({ n: this.state.n + 1 }); } render() { return ( <div className="Son"> 儿子 n: {this.state.n} <button onClick={() => this.add()}>+1</button> <Grandson /> </div> ); } } const Grandson = () => { const [n, setN] = React.useState(0); return ( <div className="Grandson"> 孙子 n:{n} <button onClick={() => setN(n + 1)}>+1</button> </div> ); }; const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
props.xxx
函数组件用useState
返回数组,第一项读,第二项写两种编程模型
Vue: 我对数据的修改会直接映射到UI上。Vue监听了n的变化,当n变化时,Vue就把n出现的地方全部都变一遍。
React: 一开始给我个state{n:0}
,我把这个state变成一个UI。 如果你想改变n,要注意不能修改之前的n。应该直接新建一个新的对象,对应一个新的UI。
React就会将这2个UI进行对比(DOM diff)区别,然后进行局部更新。
复杂 state 怎么处理
所谓复杂就是state是个对象,如果state是个复杂的对象怎么办?
如果state里不止有n怎么办?
一.类组件里有n
和m
1.类组件的setState会自动合并第一层属性:
2.当类组件里有n
和m
时,setState时可以只setState一部分,因为它会自动延用之前的属性。
3.类组件的setState不会合并第二层属性。
1.类组件的setState会自动合并第一层属性
例子
function App() { return ( <div className="App"> 爸爸 <Son /> </div> ); } class Son extends React.Component { constructor() { super(); this.state = { n: 0, m: 0 }; } addN() { this.setState({ n: this.state.n + 1 }); // m 会被覆盖为 undefined 吗? } addM() { this.setState({ m: this.state.m + 1 }); // n 会被覆盖为 undefined 吗? } render() { return ( <div className="Son"> 儿子 n: {this.state.n} <button onClick={() => this.addN()}>n+1</button> m: {this.state.m} <button onClick={() => this.addM()}>m+1</button> <Grandson /> </div> ); } } const Grandson = () => { const [n, setN] = React.useState(0); return ( <div className="Grandson"> 孙子 n:{n} <button onClick={() => setN(n + 1)}>+1</button> </div> ); }; const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
总结:类组件里的setState,如果你对其中一部分进行修改那么其他的部分会自动延用上一次的值,而不会被undefined
覆盖。
addN() { this.setState({ n: this.state.n + 1 }); /*相当于写 this.setState({...this.state, n: this.state.n + 1 }); 当只设置n时,m会自动延用上一次的值,m不会被覆盖为undefined React会自动帮你写`...this.state`:把之前所有值拷过来再将n变成n+1 */ }
2.类组件的setState不会合并第二层属性
类组件的setState会自动合并第一层属性
,但是不会合并第二层属性。
解决方法: 使用Object.assign
或者...操作符
,二选一。
1' 使用...操作符
(推荐)
class Son extends React.Component { constructor() { super(); this.state = { n: 0, //第1层,设置n时不要管m m: 0, user: { name: "frank", //第2层,设置name时必须管age age: 18 } }; } ... changeUser() { this.setState({ //m和n不会被置空 user: { ...this.state.user, //防止age被置空的解决方法 name: "jack" //坑:age会被置空,解决方法:`...this.state.user` } }); } render() { return ( <div className="Son"> 儿子 n: {this.state.n} <button onClick={() => this.addN()}>n+1</button> m: {this.state.m} <button onClick={() => this.addM()}>m+1</button> <hr /> <div>user.name: {this.state.user.name}</div> <div>user.age: {this.state.user.age}</div> <button onClick={() => this.changeUser()}>change user</button> <Grandson /> </div> ); } }
...操作法
是React经常会用到的技巧
2' 使用Object.assign
class Son extends React.Component { constructor() { super(); this.state = { n: 0, m: 0, user: { name: "frank", age: 18 } }; } changeUser() { //Object.assign把之前user的所有属性复制到新对象上 const user = Object.assign({}, this.state.user); //等价于const user={...this.state.user} user.name = "jack"; this.setState({ // m 和 n 不会被置空 user: user }); } render() { return ( <div className="Son"> 儿子 n: {this.state.n} <button onClick={() => this.addN()}>n+1</button> m: {this.state.m} <button onClick={() => this.addM()}>m+1</button> <hr /> <div>user.name: {this.state.user.name}</div> <div>user.age: {this.state.user.age}</div> <button onClick={() => this.changeUser()}>change user</button> <Grandson /> </div> ); } } const Grandson = () => { const [n, setN] = React.useState(0); return ( <div className="Grandson"> 孙子 n:{n} <button onClick={() => setN(n + 1)}>+1</button> </div> ); };
二.函数组件里有n
和m
1.函数组件的setState不会自动合并,当多个setState放一起时要记得用...操作符
把之前的复制过来。
2.函数组件的setState完全不会帮你自动合并,不管你是第一层还是第二层,要合并只能自己用...操作符。同样的需求下,用函数组件更简便,类组件已经过时了,尽量用函数组件。
例子
//第1种写法 const Grandson = () => { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); return ( <div className="Grandson"> 孙子 n:{n} <button onClick={() => setN(n + 1)}>n+1</button> m:{m} <button onClick={() => setM(m + 1)}>m+1</button> </div> ); };
函数组件另一种不推荐的写法,你会发现m被质空:
//不要这样写,当你setState`n`时,`m`就会`undefined`. const Grandson = () => { const [state, setState] = React.useState({ n:0,m:0 }); return ( <div className="Grandson"> 孙子 n:{state.n} <button onClick={() => setState({n:state.n + 1})}>n+1</button> m:{state.m} <button onClick={() => setState({m:state.m + 1})}>m+1</button> </div> ); };
如果你要用函数组件的setState就不要搞个对象在这里。如果你非要弄个对象那每次赋值时要记得先拷贝之前的。
//第2种写法 const Grandson = () => { const [state, setState] = React.useState({ n:0,m:0 }); return ( <div className="Grandson"> 孙子 n:{state.n} <button onClick={() => setState({...state,n:state.n + 1})}>n+1</button> m:{state.m} <button onClick={() => setState({...state,m:state.m + 1})}>m+1</button> </div> ); };
总结: 类组件会自动合并第1层,不会合并第2层。函数组件不会自动合并。所以不要依赖自动合并。
事件绑定
React组件里事件绑定的各种写法:onClick,onKeyPress...
一.类组件的事件绑定
<button onClick={()=>this.addN()}>
示例
//最终写法:这种写法this不会变成window class Son extends React.Component { addN=()=> this.setState({n:this.state.n+1}) //绑定 //上面的写法是下面的语法糖,2种写法完全等价 //constructor(){ // this.addN = () => this.setState({n:this.state.n+1}) //} render(){ <button onClick={this.addN()}> //使用 } }
constructor里的thisthis.addN
指的是当前对象。 这种写法函数会被定义到对象本身身上,这意味着每个Son组件都有自己的addN,如果有两个Son,就有2个addN。
//补充 //1.箭头函数上的this是不会变的,因为箭头函数不支持this addN=()=>{this.setState({n:this.state.n+1})} //2.constructor里的this`this.addN `指的是当前对象Son。 class Son extends React.Component { constructor(){ this.addN = () => this.setState({n:this.state.n+1}) } }
总结
React是面向类和函数,Vue更像是面向对象。
React提供的是JSX语法(2点):
1.普通属性用标签
2.非普通属性也就是跟JS相关的,都写在{}里。
{}之外都是标签,{}之内是JS。React与Vue的区别?
相同点
1.都是对视图的封装,React是用类和函数表示一个组件,而Vue是通过构造选项构造一个组件。
2.都提供了createElement的XML简写,React提供的是JSX语法,而Vue是提供的是template模版写法(语法巨多)
不同点
React是把HTML放在JS里写(HTML in JS),而Vue是把JS放在HTML里写(JS in HTML)