父状态更改后反应子组件未更新

Posted

技术标签:

【中文标题】父状态更改后反应子组件未更新【英文标题】:React Child Component Not Updating After Parent State Change 【发布时间】:2017-05-05 03:30:44 【问题描述】:

我正在尝试制作一个不错的 ApiWrapper 组件来填充各种子组件中的数据。从我读过的所有内容来看,这应该可以工作:https://jsfiddle.net/vinniejames/m1mesp6z/1/

class ApiWrapper extends React.Component 

  constructor(props) 
    super(props);

    this.state = 
      response: 
        "title": 'nothing fetched yet'
      
    ;
  

  componentDidMount() 
    this._makeApiCall(this.props.endpoint);
  

  _makeApiCall(endpoint) 
    fetch(endpoint).then(function(response) 
      this.setState(
        response: response
      );
    .bind(this))
  

  render() 
    return <Child data = 
      this.state.response
    
    />;
  


class Child extends React.Component 
  constructor(props) 
    super(props);
    this.state = 
      data: props.data
    ;
  

  render() 
    console.log(this.state.data, 'new data');
    return ( < span > 
      this.state.data.title
     < /span>);
  ;


var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;

ReactDOM.render(
  element,
  document.getElementById('container')
);

但由于某种原因,当父状态发生变化时,子组件似乎没有更新。

我错过了什么吗?

【问题讨论】:

【参考方案1】:

您的代码有两个问题。

你的子组件的初始状态是通过 props 设置的。

this.state = 
  data: props.data
;

引用此SO Answer:

将初始状态作为prop 传递给组件是一种反模式 因为getInitialState(在我们的例子中是构造函数)方法只在第一次调用 组件呈现。再也不会了。这意味着,如果你重新渲染 将 不同 值作为prop 传递的组件,该组件 不会做出相应的反应,因为组件会保持状态 从第一次渲染开始。这很容易出错。

所以如果你无法避免这种情况,理想的解决方案是使用方法componentWillReceiveProps 来监听新的道具。

将以下代码添加到您的子组件将解决您的子组件重新渲染问题。

componentWillReceiveProps(nextProps) 
  this.setState( data: nextProps.data );  

第二个问题是fetch

_makeApiCall(endpoint) 
  fetch(endpoint)
    .then((response) => response.json())   // ----> you missed this part
    .then((response) => this.setState( response ));

这是一个有效的小提琴:https://jsfiddle.net/o8b04mLy/

【讨论】:

"如果你知道自己在做什么,initialize state based on props 没关系" 除了 nextProp 不会在没有 @ 的情况下触发重新渲染之外,通过 prop 设置状态还有其他缺点吗987654336@? 据我所知,没有其他缺点。但是在您的情况下,我们可以清楚地避免在子组件中存在状态。父级可以将数据作为道具传递,当父级使用其新状态重新渲染时,子级也将重新渲染(使用新道具)。实际上,这里不需要在 child 内部保持状态。纯组件 FTW! 对于从现在开始阅读的任何人,请查看static getDerivedStateFromProps(nextProps, prevState)reactjs.org/docs/… 除了 componentWillReceiveProps() 因为它现在已被弃用,还有其他解决方案吗? @LearningMath 请参阅最新的React docs,其中谈到了替代方法。您可能需要重新考虑您的逻辑。【参考方案2】:

如果上述解决方案仍未解决您的问题,我建议您查看一下如何更改状态,如果您没有返回新对象,则有时 react 会发现新的以前的和更改的没有区别状态,在更改状态时始终传递一个新对象是一个好习惯,看到新对象反应肯定会重新渲染所有需要访问该更改状态的组件。

例如:-

在这里,我将在我的状态下更改一组对象的一个​​属性,看看我如何将所有数据分散到一个新对象中。另外,下面的代码对你来说可能有点陌生,它是一个 redux reducer 函数,但别担心,它只是一种改变状态的方法。

export const addItemToCart = (cartItems,cartItemToBeAdded) => 
        return cartItems.map(item => 
            if(item.id===existingItem.id)
                ++item.quantity;        
            
            // I can simply return item but instead I spread the item and return a new object
            return ...item 
        )
     

只要确保您正在使用新对象更改状态,即使您对状态进行了微小的更改,只需将其传播到新对象中然后返回,这将在所有适当的位置触发渲染。 希望这有帮助。如果我在某个地方错了,请告诉我:)

【讨论】:

【参考方案3】:

有些事情你需要改变。

fetch得到响应时,不是json。 我一直在寻找如何获得这个 json,我发现了这个 link。

另一方面,你需要认为constructor函数只被调用一次。

因此,您需要更改在&lt;Child&gt; 组件中检索数据的方式。

在这里,我留下一个示例代码:https://jsfiddle.net/emq1ztqj/

希望对你有帮助。

【讨论】:

谢谢。虽然,看看你做的例子,似乎 Child 永远不会更新。有关如何更改 Child 接收数据的方式的任何建议? 你确定吗?我看到 &lt;Child&gt; 组件从 https://jsonplaceholder.typicode.com/posts/1 检索新数据并重新渲染。 是的,我看到它现在可以在 OSX 上运行。 ios 没有触发 StackExchange 应用浏览器中的重新渲染

以上是关于父状态更改后反应子组件未更新的主要内容,如果未能解决你的问题,请参考以下文章

更改值后反应组件未更新

当提供者组件父状态发生变化时,为啥不能更新子组件中的值(带有反应上下文)?

React js从父事件中更新子状态

React 子组件在父组件中更新状态后如何将其重置回原始状态

在父组件的状态更改中重新呈现子组件

如何在反应中将最新状态从子组件传递给父组件