组件 Component
元素与组件
const div =React.createElement('div',···)
这是一个React元素(d小写)
const div =()=>React.createElement('div',···)
这是一个React组件(D大写)
React的两种组件
函数组件
function Welcome(props){
return <h1>Hello,{props.name}</h1>
}
//使用方法:<Welcome name:"Zhoujia22" />
类组件
class Welcome extends React.Component {
render() {
return <h1>Hello,{this.props.name}</h1>;
}
}
//使用方法:<Welcome name:"Zhoujia22" />
<Welcome />
会被翻译成什么
<div />会被翻译成React.createElement('div')
<Welcome />会被翻译成React.createElement(Welcome)
React.createElement的逻辑
如果传入一个字符串'div',则会创建一个div
如果传入一个函数,则会调用该函数,获取其返回值
如果传入一个类,则在类前面加个new(这会导致执行constructor),获取一个组件对象,然后调用对象的render方法,获取其返回值
小试牛刀
代码示例
添加props(外部数据)
添加props - CodeSandbox
类组件直接读取属性this.props.xxx
函数组件直接读取参数props.xxx
添加state(内部数据)
类组件用this.state读,this.setState写
setState有两种设置数据的方式,第一种是直接重新赋值一个新对象,第二种则是使用函数返回新对象
//n=0
add() {
//第一种
this.setState({ n: this.state.n + 1 });
console.log(this.state.n); //n的值为0,因为setState是异步的,执行完这一句后再去更新this.state
//第一种
this.setState((state) => {
const n = state.n + 1;
console.log(n); //n的值为1,虽然这里setState也是异步的,但是这里将新之赋值给n,这里直接读取了n
return { n }; //这里返回n之后,才更新数据
});
}
函数组件用useState返回数组,第一项读,第二项写
类组件注意事项
-
this.state.n+=1无效
其实n已经改变了,只不过UI不会自动更新而已
调用setState才会触发UI更新(异步更新)
因为React没有像Vue监听data一样监听state -
setState会异步更新UI
setState之后,state不会马上改变,立刻读state会失败
更推荐的方式是setState(函数) -
this.setState(this.state)不推荐
React希望我们不要修改旧的state(不可变数据)
常用代码:setState({n:this.state.n+1})
这是一种理念(函数式)
函数组件注意事项
-
跟类组件类似的地方
也要通过setN(新值)来更新UI -
跟类组件不同的地方
没有this,一律用参数和变量
复杂state
如果state里只有n怎么办
类组件内有n和m
函数组件内有n和m
函数组件还有一种不推荐的写法,m会被置空 (可以用...操作符合并)
总结
类组件的setState会自动合并第一层的属性
但是并不会合并第二层的属性
使用Object.assign
或者...操作符
函数组件setN则完全不会帮忙合并
事件绑定
类组件的事件绑定
<button onClick={() => this.addN()}>n+1</button>;
传给函数一个onClick即可,注意C大写
<button onClick={this.addN}>n+1</button>;
有问题这样会使得this.addN里的this变成window
<button onClick={this.addN().bind(this}>n+1</button>;
这样写是可以的,因为它返回了一个绑定当前this的新函数
不如第一种写法
但是可以声明this._addN=()=>this.addN()
然后写成
<button onClick={this._addN()}>n+1</button>;
但是又要则写一个名字
如果把函数写进constructor则是
constructor(){
this.addN=()=>this.setState({n:this.state.n+1})
}
render(){
return <button onClick={this.addN}>n+1</button>
}
但是这样写不如直接声明addN清晰
最终我们将其写成
class Son extends React.Component {
addN = () => this.setState({ n: this.state.n + 1 });
render() {
return <button onClick={this.addN}>n+1</button>;
}
}
但是这里的addN等价于写在constructor中的this.addN
两种addN的区别
class Son extends React.Component {
addN = () => this.setState({ n: this.state.n + 1 }); //第一种
addN() { //第二种
this.setState({ n: this.state.n + 1 });
}
}
//这两个addN有什么区别
第一种的addN在对象上,第二种的在原型上
并且
class Son extends React.Component {
addN = () => this.setState({ n: this.state.n + 1 });
constructor() {
this.addN = () => this.setState({ n: this.state.n + 1 });
}//以上两种完全等价
addN() {
this.setState({ n: this.state.n + 1 });
}
addN:function () {
this.setState({ n: this.state.n + 1 });
}
以上两种写法完全等价
}
两种写法的区别:
第一种函数是对象本身的属性,这意味着每个Son组件都有自己的addN,如果有两个Son,就有两个addN
第二种函数是对象的共用属性(也就是原型上的属性),这意味着所有的Son组件共用一个addN
为什么this会变?
所有函数的this都是参数,都是调用决定,所以可变
唯独箭头函数的this不变,因为箭头函数不接受this,在声明是就确定为实例。