子组件不会重新渲染,但父组件会重新渲染。如何让子组件重新渲染?

Posted

技术标签:

【中文标题】子组件不会重新渲染,但父组件会重新渲染。如何让子组件重新渲染?【英文标题】: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没有被解雇的原因。这就是问题所在。有关更多信息,请查看下面的答案。

【问题讨论】:

【参考方案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

【讨论】:

以上是关于子组件不会重新渲染,但父组件会重新渲染。如何让子组件重新渲染?的主要内容,如果未能解决你的问题,请参考以下文章

如何在反应原生的子组件上重新渲染父组件?

React 子组件不会在其状态更改时重新渲染

React 啥时候重新渲染子组件?

解决vue开发时子组件数据和组件渲染的异步问题

state 用作组件中的 props,状态更改不会重新渲染组件

Vue 组件 prop 更改不会触发重新渲染