美文网首页react
React学习 —— 受控组件、生命周期、PureCompone

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

作者: yozosann | 来源:发表于2019-05-26 14:13 被阅读34次

    受控组件与非受控组件

    受控组件:

    在 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 中,构造函数仅用于以下两种情况:

    • 通过给 this.state 赋值对象来初始化内部 state
    • 事件处理函数绑定实例
      如果不做这些处理,则不需要constructor()
      避免将 props 的值复制给 state!这是一个常见的错误!!
    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要求满足如下条件:

    • props和state都不可变
    • props和state没有层级
    • 如果数据改变无法反应在浅拷贝上,应该调用forceUpdate更新。

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

    参考文献

    https://react.docschina.org/

    相关文章

      网友评论

        本文标题:React学习 —— 受控组件、生命周期、PureCompone

        本文链接:https://www.haomeiwen.com/subject/brvxtctx.html