【问题标题】:Child component doesn't rerender but parent component does rerender. How to make child component rerender?子组件不会重新渲染,但父组件会重新渲染。如何让子组件重新渲染?
【发布时间】:2020-02-13 17:49:55
【问题描述】:

父组件在收到新的 props 时会重新渲染,但其子组件不会重新渲染。子组件仅第一次渲染,从不重新渲染,也不从 redux store 接收 props

我在父组件中从 redux 存储中获取更新的数据,但在子组件中没有。子组件只有在第一次渲染时才从 redux store 接收数据

我的父组件Home.js

对象 seaFCLJSON 看起来像这样 const seaFCLJSON ={"rates": {"sort":"faster", "someOther": "someOtherValues"}};

当 redux 存储更新时,seaFCLJSON 看起来像这样 const seaFCLJSON ={"rates": {"sort":"cheaper","someOther": "someOtherValues"}};

class Home extends Component {
state = {
    seaFCLJSON: {}
  };

componentDidMount = () => {
     this.setState({ seaFCLJSON: this.props.seaFCLJSON });
  };

 componentWillReceiveProps = nextProps => {
    if (this.state.seaFCLJSON !== nextProps.seaFCLJSON) {
      this.setState({ seaFCLJSON: nextProps.seaFCLJSON });
    }
  };

 render() {
    const { seaFCLJSON } = this.props;
 return (
       <>
    {!isEmpty(seaFCLJSON) && seaFCLJSON.rates && seaFCLJSON.rates.fcl ? (
          <FCLContainer fclJSON={seaFCLJSON} />
        ) : null} //it never rerenders upon getting updated data from redux store

    <h5>{JSON.stringify(seaFCLJSON.rates && seaFCLJSON.rates.sort)}</h5> //it rerenders everytime upon getting updated data from redux store
       </>
     );
  }
}

const mapStateToProps = state => {
  return {
    seaFCLJSON: state.route.seaFCLJSON
  };
};

export default connect(
  mapStateToProps,
  actions
)(Home);

isEmpty.js

export const isEmpty = obj => {
  return Object.entries(obj).length === 0 && obj.constructor === Object;
};

我的子组件FCLContainer.js

class FCLContainer extends Component {
  state = {
    seaFCLJSON: {}
  };
  componentDidMount = () => {
    this.setState({ seaFCLJSON: this.props.seaFCLJSON });
  };

  componentWillReceiveProps = nextProps => {
    console.log("outside state value: ", this.state.seaFCLJSON);
    if (this.state.seaFCLJSON !== nextProps.seaFCLJSON) {
      this.setState({ seaFCLJSON: nextProps.seaFCLJSON });
      console.log("inside state value: ", this.state.seaFCLJSON);
    }
  };


  render() {
    const { seaFCLJSON } = this.state;
    console.log("rendering .. parent props: ", this.props.fclJSON);
    console.log("rendering .. redux store props: ", this.props.seaFCLJSON);

    return (
      <>
        <div className="home-result-container">
          <div>
          <h5>This child component never rerenders :(</h5>
          </div>
        </div>
      </>
    );
  }
}

const mapStateToProps = state => {
  return {
    seaFCLJSON: state.route.seaFCLJSON
  };
};

export default connect(
  mapStateToProps,
  actions
)(FCLContainer);

不知道是Parent组件有问题还是子组件有问题。 componentWillReceiveProps 在父组件中被调用,但在子组件中不被调用。请忽略任何缺少的分号或大括号,因为我省略了一些不必要的代码。 编辑 1:我只是出于调试目的将 props 中的值复制到 state 中。

感谢您的帮助。谢谢。

编辑 2:我在 redux 操作中直接更改了一个对象。这就是CWRP没有被解雇的原因。这就是问题所在。有关更多信息,请查看下面的答案。

【问题讨论】:

    标签: javascript reactjs redux react-redux redux-thunk


    【解决方案1】:

    componentWillReceiveProps 将在 react 17 中被弃用,使用 componentDidUpdate 代替,它在更新发生后立即调用 试试这样的:

    componentDidUpdate(prevProps, prevState) {
        if (this.prevProps.seaFCLJSON !== this.props.seaFCLJSON) {
          this.setState({ seaFCLJSON: this.props.seaFCLJSON });
        }
      };
    

    【讨论】:

    • 我试过了。它没有解决我的问题。它没有被调用。它只会在第一次被调用,并且永远不会被更新的 props 调用
    • @blueseal 你能用代码创建一个代码沙箱吗?
    • 对不起,我试图复制它但不能,后来因为我的项目很大而放弃了它。
    【解决方案2】:

    首先,将值从 props 复制到 state 绝对没有意义,这是什么意思?完全没有意义,把它放在道具里

    关于您的问题 - 很可能此条件不匹配,这就是子组件不触发的原因

    !isEmpty(seaFCLJSON) && seaFCLJSON.rates && seaFCLJSON.rates.fcl
    

    在调试器中检查它

    【讨论】:

    • 已经深夜了。我明天试试,让你知道..谢谢!
    • 我只是将 props 中的值复制到 state 中,仅用于调试目的。 if(!isEmpty(seaFCLJSON) &amp;&amp; seaFCLJSON.rates &amp;&amp; seaFCLJSON.rates.fcl){ console.log("everything is otay");} 它确实向控制台输出“一切都是 otay”
    【解决方案3】:

    据我所知,您的问题是您将以下内容传递给您的子组件:

    <FCLContainer fclJSON={seaFCLJSON} />
    

    但你假设你收到了一个名为“seaFCLJSON”的道具:

     componentDidMount = () => {
    this.setState({ seaFCLJSON: this.props.seaFCLJSON });
    

    };

    您应该将代码更改为:

    <FCLContainer seaFCLJSON={seaFCLJSON} />
    

    除此之外,正如@Paul McLoughlin 已经提到的,您应该直接使用该道具,而不是将其添加到您的状态中。

    【讨论】:

    • 我不需要将任何道具从父组件传递给我的子组件,因为我从 redux 存储中获取了更新的值(仅适用于第一次渲染)。我刚刚将 prop fclJSON 传递给子组件以进行调试。不知道为什么子组件收不到来自 redux 存储的更新数据
    • 我能够从子组件中的父组件获取更新的道具。但是知道为什么孩子没有从 redux 商店获得更新的数据,但父母却得到了相同的数据吗?在我的实际项目中,子组件确实有自己的子组件,所以我应该避免道具钻探。
    • 据我所知,如果用this.setState修改状态或者挂载了组件,componentWillReceiveProps是不会被调用的。如果您在父组件中的渲染条件(无论出于何种原因)从 true 变为 false 并再次变回,则重新安装该组件并且永远不会调用 componentWillReceiveProps。请参阅reactjs.org/docs/react-component.html 另外,我将删除所有日志记录代码,只需将数据从父级传递给子级(不将子级连接到 redux 状态)并确保在子级中渲染一些道具。
    • 你有存储库或类似的东西吗?这实际上应该是一个非常简单的案例,但我觉得我错过了一些信息。只要 Store 提供者包装了这些组件,它应该是一个从子节点连接的问题。
    • 我把父组件的渲染条件去掉,移到了子组件。但是,我仍然没有从商店获得更新的数据。我会尝试更多的调试,然后我会让你知道,并可能邀请你到我的仓库。谢谢你的帮助:)
    【解决方案4】:

    我发现了我在操作中直接改变对象的问题。我只知道state 不应该在classreducer 内部直接变异。我更改了直接更改对象的操作,然后通过 dispatch 将其保存在 redux 存储中,然后我在 CWRP 中收到了更新的道具。这真的花了我很多次才弄明白。这种问题至少对我来说很难找到。我想我是从https://github.com/uberVU/react-guide/issues/17得到这个的

    我学到的一个教训:永远不要直接改变对象

    我改变了这个

    //FCL sort by faster
    export const sortByFasterFCLJSON = () => async (dispatch, getState) => {
      let seaFCLJSON = getState().route.seaFCLJSON;
      if (!seaFCLJSON.rates) return;
      seaFCLJSON.rates.fcl = _.orderBy(
        seaFCLJSON.rates.fcl,
        ["transit_time"],
        ["asc"]
      );
    
      seaFCLJSON.rates.sort = "Faster"; //this is the main culprit
    
      dispatch({ type: SET_SEA_FCL_JSON, payload: seaFCLJSON });
    };
    

    到此

    //FCL sort by faster
    export const sortByFasterFCLJSON = () => async (dispatch, getState) => {
      let seaFCLJSON = getState().route.seaFCLJSON;
      if (!seaFCLJSON.rates) return;
      seaFCLJSON.rates.fcl = _.orderBy(
        seaFCLJSON.rates.fcl,
        ["transit_time"],
        ["asc"]
      );
    
      // seaFCLJSON.rates.sort = "Faster"; //this was the main culprit, got lost
    
      seaFCLJSON = {
        ...seaFCLJSON,
        rates: { ...seaFCLJSON.rates, sort: "Faster" }
      };
    
      dispatch({ type: SET_SEA_FCL_JSON, payload: seaFCLJSON });
    };
    
    

    the power of not mutating data

    旁注:Redux Troubleshooting

    【讨论】: