react

React学习 —— 受控组件、生命周期、PureCompone

2019-05-26  本文已影响34人  yozosann

受控组件与非受控组件

受控组件:

在 HTML 中,表单元素(如<input><textarea><select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

该组件实现了一个点击提交,打印名字的功能。
有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数(因为数据都存在state里,所以需要编写事件处理函数更新state,就比如这里都handleChange),并通过一个 React 组件传递所有的输入 state。

非受控组件

要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

这里和受控组件都区别就在于,我们并没有给input赋值(value={this.state.xxx}),数据本身都是存储在dom节点本身的,每次我们需要获取或者改变的时候我们应该直接去通过ref操作dom节点,而非设置state。

默认值

如果是受控组件,value就可以充当默认值,而非受控组件由于没有可以传递的地方本应该是除了直接操作dom之外没有设置默认值的方法的,但是react提供了封装,我们可以在dom上添加default值:

render() {
  return (
    <form onSubmit={this.handleSubmit}>
      <label>
        Name:
        <input
          defaultValue="Bob"
          type="text"
          ref={this.input} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}

生命周期

常用的生命周期方法

本节中的方法涵盖了创建 React 组件时能遇到的绝大多数用例。想要更好了解这些方法,可以参考生命周期图谱

render()

只能返回:React、fragments、Portals、字符串或者数值、布尔类型或 null。

constructor()

通常,在 React 中,构造函数仅用于以下两种情况:

constructor(props) {
 super(props);
 // 不要这样做
 this.state = { color: props.color };
}

componentDidMount()

会在组件挂载后(插入 DOM 树中)立即调用。
这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅。

你可以在 componentDidMount() 里可以直接调用 setState()。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render() 两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在 constructor() 中初始化 state。如果你的渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理

componentDidUpdate()

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 会在更新后会被立即调用。首次渲染不会执行此方法。
当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)。

componentWillUnmount()

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。
componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

shouldComponentUpdate()

当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。
此方法仅作为性能优化的方式而存在。不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug。

我们不建议在 shouldComponentUpdate() 中进行深层比较或使用 JSON.stringify()。这样非常影响效率,且会损害性能。

其他API

setState()

setState(updater[, callback])

将 setState() 视为请求而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。
setState() 并不总是立即更新组件。它会批量推迟更新。这使得在调用 setState() 后立即读取 this.state 成为了隐患。为了消除隐患,请使用 componentDidUpdate 或者 setState 的回调函数(setState(updater, callback)),这两种方式都可以保证在应用更新后触发。如需基于之前的 state 来设置当前的 state,请阅读下述关于参数 updater 的内容。

this.setState((state, props) => {
  return {counter: state.counter + props.step};
});

updater 函数中接收的 state 和 props 都保证为最新。updater 的返回值会与 state 进行浅合并。
有关更多详细信息,请参阅:

forceUpdate()

默认情况下,当组件的 state 或 props 发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染。
调用 forceUpdate() 将致使组件调用 render() 方法,此操作会跳过该组件的 shouldComponentUpdate()。

ReactDOM

react-dom 的 package 提供了可在应用顶层使用的 DOM(DOM-specific)方法,如果有需要,你可以把这些方法用于 React 模型以外的地方。不过一般情况下,大部分组件都不需要使用这个模块。

React.Component与React.PureComponent

React.PureComponentReact.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。

shouldComponentUpdate在刚刚的生命周期中也说过,当props或者state发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。
返回默认值true,首次渲染和 forceUpdate() 时不会调用该方法。

如果 shouldComponentUpdate() 返回 false,则不会调用 UNSAFE_componentWillUpdate()render()componentDidUpdate()
而React.Component的shouldComponentUpdate默认就是返回true的,React.PureComponent是实现一套逻辑,浅比较props和state,并减少了跳过必要更新的可能性。

什么是浅比较?
对于基本类型(primitives),例如数字或者布尔值,来说,浅拷贝将会检查其值是否相同,例如1与1相等,true与true相等。对于引用类型的变量,例如复杂的javascript对象或者数组,来说,浅拷贝将仅仅检查它们的引用值是否相等。这意味着,对于引用类型的变量来说,如果我们只是更新了其中的一个元素,例如更新了数组中某一位置的值,那么更新前后的数组仍是相等的。

因此意味着相比于Component,PureCompoent的性能表现将会更好。但使用PureCompoent要求满足如下条件:

React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 props 和 state 较为简单时,才使用 React.PureComponent,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。

参考文献

https://react.docschina.org/

上一篇 下一篇

猜你喜欢

热点阅读