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