React类组件和函数组件

84 阅读4分钟

组件 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方法,获取其返回值

小试牛刀

代码示例

函数组件和类组件 - CodeSandbox

添加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两个值 - CodeSandbox

函数组件内有n和m

函数组件内有n和m - CodeSandbox

函数组件还有一种不推荐的写法,m会被置空 (可以用...操作符合并)

函数组件不推荐写法 - CodeSandbox

总结

类组件的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,在声明是就确定为实例。