父状态更改后,React子组件不会更新

Vin*_*mes 84 reactjs

我正在尝试制作一个漂亮的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')
);
Run Code Online (Sandbox Code Playgroud)

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

我在这里错过了什么吗?

Yad*_*ran 167

您的代码有两个问题.

您的子组件的初始状态是从props设置的.

this.state = {
  data: props.data
};
Run Code Online (Sandbox Code Playgroud)

引用此SO答案:

将初始状态作为prop反模式传递给组件是因为getInitialState(在我们的情况下是构造函数)方法仅在组件第一次呈现时被调用.从来没有.这意味着,如果重新呈现该组件传递不同的值作为a prop,则组件将不会相应地做出反应,因为组件将从第一次呈现时保持状态.这很容易出错.

因此,如果您无法避免这种情况,理想的解决方案是使用该方法componentWillReceiveProps来收听新的道具.

将以下代码添加到子组件将解决Child组件重新呈现的问题.

componentWillReceiveProps(nextProps) {
  this.setState({ data: nextProps.data });  
}
Run Code Online (Sandbox Code Playgroud)

第二个问题是与fetch.

_makeApiCall(endpoint) {
  fetch(endpoint)
    .then((response) => response.json())   // ----> you missed this part
    .then((response) => this.setState({ response }));
}
Run Code Online (Sandbox Code Playgroud)

这是一个工作小提琴:https://jsfiddle.net/o8b04mLy/

  • 据我所知,没有其他缺点.但在你的情况下,我们可以清楚地避免在子组件中有一个状态.父级可以将数据作为道具传递,当父级重新呈现其新状态时,子级也将重新渲染(使用新的道具).实际上这里不需要维持儿童内部的状态.纯组件FTW! (11认同)
  • 对于从现在开始阅读的任何人,请检查`static getDerivedStateFromProps(nextProps,prevState)`https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops (7认同)
  • 除了componentWillReceiveProps()之外还有其他解决方案,因为它现在已被弃用了吗? (4认同)
  • @LearningMath,请参阅最新的[React docs](https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops),其中介绍了其他方法。您可能需要重新考虑自己的逻辑。 (2认同)
  • 只是想留下一条评论,提醒大家注意 `componentWillReceiveProps` 将在版本 17 中被删除。他们建议使用 `getDerivedStateFromProps` 作为替代品。希望它能帮助人们! (2认同)

小智 6

如果上述解决方案仍未解决您的问题,我建议您查看一下如何更改状态,如果您没有返回新对象,那么有时 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} 
        })
    } 
Run Code Online (Sandbox Code Playgroud)

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