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

我正在尝试创建一个漂亮的 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')
);

但出于某种原因,当父级状态更改时,子组件似乎没有更新。

我错过了什么吗?

155573 次浏览

有些事情你需要改变。

fetch得到响应时,它不是一个 json。 我正在寻找如何得到这个 json,我发现了这个 链接

另一方面,您需要考虑 constructor函数只调用一次。

因此,您需要更改在 <Child>组件中检索数据的方式。

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

希望能帮上忙。

您的代码有两个问题。

子组件的初始状态是从道具设置的。

this.state = {
data: props.data
};

引自 回答我:

将初始状态作为 prop传递给组件是反模式 因为 getInitialState(在我们的例子中是构造函数)方法只在 组件呈现。从来没有更多。这意味着,如果你重新呈现 将 与众不同值作为 prop传递的组件 将不会作出相应的反应,因为组件将保持 从它第一次被渲染开始,它非常容易出错。

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

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

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/

如果上面的解决方案仍然没有解决你的问题,我建议你看一下你是如何改变状态的,如果你没有返回一个新的对象,那么有时候反应看不到新的前一个对象和改变的状态有什么不同,这是一个很好的做法,总是传递一个新的对象,当改变状态时,看到新的对象反应一定会重新呈现所有需要访问改变状态的组件。

例如:-

在这里,我将更改处于我的状态的对象数组的一个属性,看看我如何在一个新对象中展开所有数据。另外,下面的代码对你来说可能看起来有点陌生,它是一个 redux reduce 函数,但是不要担心,它只是一个改变状态的方法。

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}
})
}

只要确保用一个新对象改变状态,即使对状态做了很小的改变,也只是将其传播到一个新对象中,然后返回,这将在所有适当的位置触发呈现。 希望这对你有帮助。如果我错了请告诉我:)

接受答案和 componentWillReceiveProps

在接受的应答中的 componentWillReceiveProps呼叫是不推荐的,将从17版 React Docs: UNSAFE_componentWillReceiveProps()反应文件: UNSAFE_componentWillReceiveProps()中删除

在 React 中使用派生状态逻辑

正如 React 文档所指出的,使用派生状态(意思是: 一个组件反映其道具中发生的变化)会使您的组件更难以思考,并且可能是反模式。React Docs: 您可能不需要派生状态

目前的解决方案: getDerivedStateFromProps

如果您选择使用派生状态,当前的解决方案是使用 getDerivedStateFromProps调用@DiogoSanto 说。

在初始挂载和随后的更新中,在调用呈现方法之前都会调用 getderiedStateFromProps。它应该返回一个对象来更新状态,或者返回 null 来不更新任何内容。反应文件: static getDerivedStateFromProps()

如何使用 componentWillReceiveProps

此方法不能访问实例属性。它所做的一切描述反应如何计算新的状态从一个给定的道具。无论何时更改道具,React 将调用此方法,并将使用此方法返回的对象作为新状态。

class Child extends React.Component {
constructor() {
super(props);
// nothing changed, assign the state for the
// first time to teach its initial shape.
// (it will work without this, but will throw
// a warning)
this.state = {
data: props.data
};
}


componentWillReceiveProps(props) {
// return the new state as object, do not call .setState()
return {
data: props.data
};
}


render() {
// nothing changed, will be called after
// componentWillReceiveProps returned the new state,
// each time props are updated.
return (
<span>{this.state.data.title}</span>
);
}
}

注意

  • 根据父组件中发生的更改重新呈现组件可能会让用户感到烦恼,因为会丢失该组件上的用户输入。
  • 派生的状态逻辑会使组件更难理解、更难思考。