React:组件

246 阅读4分钟

类组件和函数组件

Element V.S. Component

元素与组件

// React 元素(d小写)
const div = React.createElement('div',...)
​
// React 组件(D大写)
const Div = () => React.createElement('div'..)

什么是组件

能跟其他物件组合起来的物件,就是组件

就目前而言,一个返回 React 元素的函数就是组件

在 Vue 里,一个构造选项就可以表示一个组件

React 两种组件

函数组件

function Welcome(props){
  return <h1>Hello,{props.name}</h1>
}
// 使用方法: <Welcome name="river"/>

类组件

class Welcome extends React.Component {
  render() {
    return <h1>Hello,{this.props.name}</h1>
  }
}
// 使用方法: <Welcome name="river"/>

<Welcome />

会被翻译成什么

<div />会被翻译为 React.createElement('div')

<Welcome />会被翻译为 React.createElement(Welcome)

babel online

React.createElement 的逻辑

  • 传一个字符串'div',会创建一个div
  • 传一个函数,会调用该函数,获取其返回值
  • 传一个,会在类前面加个new(这会导致执行constructor),获取一个组件对象,然后调用对象的render方法,获取其返回值

使用 props 和 state

小试牛刀

代码示例

+1

添加 props (外部数据)

示例

类组件直接读取属性 this.props.xxx

函数组件直接读取参数 props.xxx

添加 state (内部数据)

示例

类组件this.state读,this.setState

函数组件useState返回数组,第一项读,第二项写

注意:setState会等一会再改变n,而setN不会改变n

注意事项

类组件注意事项

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: state.n+1 })

总结

这是一种理念(函数式

函数组件注意事项

跟类组件类似的地方

也要通过setX(新值)来更新UI

跟类组件不同的地方

没有this,一律用参数和变量

两种编程模型

Vue 的编程模型

一个对象,对应一个虚拟DOM。当对象的属性改变时,把属性相关的DOM节点全部更新。

注:Vue为了其他考量,也引入了虚拟DOM和DOM diff,此图只是简化

React 的编程模型

一个对象,对应一个虚拟DOM。另一个对象,也对应另一个虚拟DOM。

对比两个虚拟DOM,找不同(DOM diff),最后局部更新DOM。

复杂 state

如果state里不止有n怎么办

类组件里有n和m

注意:

image-20210727013413663.png

class 组件里的setState,如果对其中一部分进行修改,那么其他部分会自动沿用上一次的值,不会被undefined覆盖。

函数组件里有n和m

函数组件有另一种不推荐写法,m被空置。(需要用...操作符

总结

类组件的setState会自动合并第一层属性,但是并不会合并第二次属性,那怎么办?

使用Object.assign或者...操作符

函数组件的setX则完全不会帮助合并,如果合并需要自己用...操作符合并

绑定事件

onClick、onKeyPress...

类组件的事件绑定

<button onClick={() => this.addN()}>n+1</button>
// 传一个函数给 onClick 即可
​
<button onClick={this.addN}>n+1</button>
// 有问题,这样会使得 this.addN 里的 this 变成 window
​
<button onClick={this.addN.bind(this)}>n+1</button>
// 这样写可以,因为它返回了一个绑定了当前this的新函数
​
<button onClick={this._addN}>n+1</button>
// 在constructor中声明 this._addN = ()=>this.addN() ,即给箭头函数取一个名字

上面的第四种写法继续优化,直接写成:

constructor(){
    this.addN = () => this.setState({n: this.state.n+1})
}
render(){
    return <button onClick={this.addN}>n+1</button>
}

最终写法

class Son extends React.Component{
  addN = () => this.setState({n: this.state.n + 1});
  render(){
    return <button onClick={this.addN}>n+1</button>
  }
}
// 使用新语法,addN直接写在class里,且不需要写this
// 这里的 addN 等价于上面的 constructor 中的 addN

那么问题来了

class Son extends React.Component{
  addN = () => this.setState({n: this.state.n + 1})
  addN(){
      this.setState({n: this.state.n + 1})
  }
}
// 这两个 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 写法完全等价
addN(){
  this.setState({n: this.state.n + 1})
}
add: function(){
  this.setState({n: this.state.n + 1})
}
// 两个 addN 写法完全等价

两组 addN 的区别

第一组 addN 在对象上,第二组 addN在原型上

第二组的this可变成window,第一组不会

两个例子

例1

image-20210727032052742.png

箭头函数定义会放在对象自己身上

例2

image-20210727031439749.png

上面例子可以得出一个结论,所有定义到class的方法都会被放到对象的原型上

区别

第一组函数是对象本身的属性,这意味着每个Son组件都有自己的addN,如果有两个Son,就有两个addN

第二组函数是对象的共用属性(也就是原型上的属性),这意味着所有Son组件共用一个addN

为什么 this会变 / 不会变

所有函数的this都是参数,由调用决定,所以可变

唯独箭头函数的this不变,因为箭头函数不接受this

结论

优先使用函数组件,完全不用this

如果使用类组件,那么用第一组的方法更好