• state 和 props 同时改变子组件状态方法


    使用 componentDidUpdate 生命周期进行监听 props 的变化

    方案一

    比如我有一种场景 A 组件的 state 初始值是 props 传入的值:

    this.state = {
      counter: this.props.initNum,
    };
    

    然后在组件内部可以改变 this.state.counter 来改变子组件内容。
    这时候需要改变父组件的 props.initNum 同样可以改变 state.counter 的值:
    父组件:

    import "./App.css";
    import React from "react";
    import MouseTracker from "./Components/MouseTracker";
    
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          counter: 10,
        };
      }
      addParentNum() {
        let curr = this.state.counter;
        this.setState({
          counter: curr + 1,
        });
      }
      render() {
        return (
          <div className="App">
            <MouseTracker initNum={this.state.counter} />
            <button onClick={() => this.addParentNum()}>父元素增加num</button>
          </div>
        );
      }
    }
    
    export default App;
    

    对应的子组件

    import React from "react";
    class MouseTracker extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          counter: this.props.initNum,
        };
      }
      componentDidUpdate(prevProps) {
        if (prevProps.initNum !== this.props.initNum) {
          this.setState({
            counter: this.props.initNum,
          });
        }
      }
      addNum() {
        let counter = this.state.counter;
        console.log(counter);
        this.setState({
          counter: counter + 1,
        });
      }
      render() {
        return (
          <div>
            <div>{this.state.counter}</div>
            <button onClick={() => this.addNum()}>点击切换</button>
          </div>
        );
      }
    }
    
    export default MouseTracker;
    

    方案二

    React 的 16.3 版本中对生命周期进行了较大的调整,这是为了开发者能正确地使用生命周期,避免误解其概念而造成反模式。

    本文将重点介绍 getDerivedStateFromProps 这个生命周期。要注意的是,React 16.3 的版本中 getDerivedStateFromProps 的触发范围是和 16.4^ 是不同的,主要区别是在 setState 和 forceUpdate 时会不会触发,具体可以看这个生命全周期图 。
    该方法替换了 componentwillmount 和 componentwilllUpdate 使用时需要考虑好

    用法

    getDerivedStateFromProps exists for only one purpose. It enables a component to update its internal state as the result of changes in props.

    从上边这句话中,我们可以清晰知道 getDerivedStateFromProps 的作用就是为了让 props 能更新到组件内部 state 中。所以它可能的使用场景有两个:

    无条件的根据 prop 来更新内部 state,也就是只要有传入 prop 值, 就更新 state
    只有 prop 值和 state 值不同时才更新 state 值。
    我们接下来看几个例子。

    假设我们有个一个表格组件,它会根据传入的列表数据来更新视图。

    class Table extends React.Component {
        state = {
            list: []
        }
        static getDerivedStateFromProps (props, state) {
            return {
                list: props.list
            }
        }
        render () {
            .... // 展示 list
        }
    }
    

    上面的例子就是第一种使用场景,但是无条件从 prop 中更新 state,我们完全没必要使用这个生命周期,直接对 prop 值进行操作就好了,无需用 state 值类保存。

    在看一个例子,这个例子是一个颜色选择器,这个组件能选择相应的颜色并显示,同时它能根据传入 prop 值显示颜色。

    Class ColorPicker extends React.Component {
        state = {
            color: '#000000'
        }
        static getDerivedStateFromProps (props, state) {
            if (props.color !== state.color) {
                return {
                    color: props.color
                }
            }
            return null
        }
        ... // 选择颜色方法
        render () {
            .... // 显示颜色和选择颜色操作
        }
    }
    

    现在我们可以这个颜色选择器来选择颜色,同时我们能传入一个颜色值并显示。但是这个组件有一个 bug,如果我们传入一个颜色值后,再使用组件内部的选择颜色方法,我们会发现颜色不会变化,一直是传入的颜色值。

    这是使用这个生命周期的一个常见 bug。为什么会发生这个 bug 呢?在开头有说到,在 React 16.4^ 的版本中 setState 和 forceUpdate 也会触发这个生命周期,所以内部 state 变化后,又会走 getDerivedStateFromProps 方法,并把 state 值更新为传入的 prop。

    接下里我们来修复这个 bug。

    Class ColorPicker extends React.Component {
        state = {
            color: '#000000',
            prevPropColor: ''
        }
    static getDerivedStateFromProps (props, state) {
        if (props.color !== state.prevPropColor) {
            return {
                color: props.color
                prevPropColor: props.color
            }
        }
        return null
    }
    ... // 选择颜色方法
    render () {
    .... // 显示颜色和选择颜色操作
    }
    }
    

    通过保存一个之前 prop 值,我们就可以在只有 prop 变化时才去修改 state。这样就解决上述的问题。

    这里小结下 getDerivedStateFromProps 方法使用的注意点:

    在使用此生命周期时,要注意把传入的 prop 值和之前传入的 prop 进行比较。
    因为这个生命周期是静态方法,同时要保持它是纯函数,不要产生副作用。
    上述的情况在大多数情况下都是适用,但是这边还是会有产生 bug 的风险。具体可以官网提供这个例子。在 One 和 Two 的默认账号都相同的情况下,使用同一个输入框组件,在切换到 Two,并不会显示成 Two 的默认账号。

    这边解决方法有四种:

    第一种是将组件改成完全可控组件(也是状态值和方法全由父类控制);

    第二种是改成完全不可控组件(也就是组件不接受在 getDerivedStateFromProps 中通过 prop 值来改变内部状态),然后通过设置在构造函数中把 prop 传给 state 和设置 key 值来处理,因为 key 变化的时候 React 会重新渲染组件,而不是去更新组件。

    第三种还是保持上述组件模式,然后通过一个唯一 ID 来判断是否更新,而不是通过 color 值来判断。

    第四种不使用 getDerivedStateFromProps,通过 ref 来把改变邮箱的方法暴露出去。

    反模式
    常见的反模式有两种,上边也有提到过。

    无条件地根据 prop 值来更新 state 值
    当 prop 值变化并且和 state 不一样时就更新 state (会造成内部变化无效,上述也提到过)。
    总结
    我们应该谨慎地使用 getDerivedStateFromProps 这个生命周期。我个人使用情况来说,使用时要注意下面几点:

    因为这个生命周期是静态方法,同时要保持它是纯函数,不要产生副作用。
    在使用此生命周期时,要注意把传入的 prop 值和之前传入的 prop 进行比较(这个 prop 值最好有唯一性,或者使用一个唯一性的 prop 值来专门比较)。
    不使用 getDerivedStateFromProps,可以改成组件保持完全不可控模式,通过初始值和 key 值来实现 prop 改变 state 的情景。
    更多详细内容可以阅读官网 Blog。
    React 中 getDerivedStateFromProps 的用法和反模式

    ===============
    子组件:

    import React from "react";
    class ColorPicker extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          color: this.props.color,
          prevPropColor: "",
          currName: "花哈呼",
        };
      }
    
      static getDerivedStateFromProps(props, state) {
        console.log(
          "props.color=" + props.color,
          "state.prevPropColor=" + state.prevPropColor,
          "state.color=" + state.color
        ); //green red
        if (props.color !== state.prevPropColor) {
          return {
            color: props.color,
            prevPropColor: props.color,
          };
        }
        return null;
      }
    
      changeColor() {
        this.setState({
          color: "orange",
          currName: "huahua",
        });
      }
      render() {
        return (
          <>
            <div style={{ color: this.state.color }}>
              颜色是:{this.state.color}
              姓名是:{this.state.currName}
            </div>
            <button onClick={() => this.changeColor()}>点击改变颜色</button>
          </>
        );
      }
    }
    export default ColorPicker;
    

    父组件:

    
    import React from "react";
    import ColorPicker from "./ColorPicker";
    export default class MouseTracker extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          counter: 10,
          isShow: 1,
          parentColor: "red",
        };
      }
      changeColor() {
        let parentColor = this.state.parentColor;
        this.setState({
          parentColor: parentColor === "red" ? "green" : "red",
        });
      }
      render() {
        return (
          <div className="App">
            <ColorPicker color={this.state.parentColor} />
            <br />
            <button
              onClick={() => {
                this.changeColor();
              }}
            >
              点击父元素改变颜色
            </button>
          </div>
        );
      }
    }
    

    解析
    每次 props.color 和 state.prevPropColor 不一样的时候 state.prevPropColor 等于 props.color;
    而 state.color 一直等于子组件内部的变化

    初始值:props.color=red state.prevPropColor= state.color=#000000
    点击子组件状态:props.color=red;state.prevPropColor=red;state.color=orange

    static getDerivedStateFromProps(props, state) {
        return null;
    }
    

    点击父组件状态:props.color=green state.prevPropColor=red state.color=orange

    
    static getDerivedStateFromProps(props, state) {
        console.log("props.color=green state.prevPropColor=red state.color=orange")//此时state尚未变化
        if (props.color !== state.prevPropColor) { // green !== red
            return {
                color: props.color,// green
                prevPropColor: props.color,// green
            };
        }
        return null;
    }
    

    props.color=green state.prevPropColor=red state.color=green

    再次点击子组件改变状态:
    props.color=green state.prevPropColor=green state.color=orange
    props.color === state.prevPropColor,说明是子组件改变了 state,props 没有变化

    也就是点击子组件中的按钮触发事件,会先改变 state,然后会触发 getDerivedStateFromProps 事件,
    因为此时 props 没有发生变化,也就是 props.color === state.prevPropColor ,则返回 null,不做任何改变;

    如果点击了父组件中的按钮,改变了子组件传入的 props,则触发 getDerivedStateFromProps 事件,
    则 props.color !== state.prevPropColor ,则返回

    {
        color: props.color,
        prevPropColor: props.color,
    }
    

    从而改变 state 状态

  • 相关阅读:
    windows 全局安装 composer【转】
    CentOS7使用firewalld打开关闭防火墙与端口
    转 Page Object模式
    转 用SQL语句,删除掉重复项只保留一条
    转 什么是Mbps、Kbps、bps、kb、mb及其换算和区别
    转 使用Python的logging.config.fileConfig配置日志
    转 zookeeper,dubbo和Nginx的区别
    转 lsof命令详解
    Fatal Error -26000: Not enough memory (12320 bytes) for “new buffer in LrwSrvNetTaskIt 问题解决及lr脚本心得
    Action.c(28): Error -27796: Failed to connect to server "xxxx": [10060] Connection timed out
  • 原文地址:https://www.cnblogs.com/xiaozhumaopao/p/14376903.html
Copyright © 2020-2023  润新知