React生命周期钩子疑问:组件如何基于父组件异步数据正确渲染?
看起来你卡在了父组件异步获取数据后,子组件无法同步更新并渲染嵌套结构的问题上,我来帮你理清楚问题出在哪,以及怎么解决。
问题根源
你的核心问题是:子组件把props.componentsComponents存到了自己的state里,但只在constructor或者挂载钩子(componentDidMount/componentWillMount)里初始化了一次。而父组件的arrayOfComponents是异步获取的,初始是空数组,等数据回来后才更新——这时候子组件的props已经变了,但你的子组件没有监听props的变化,所以state一直停留在初始的空数组状态,自然渲染不出内容。
另外,componentWillMount已经被废弃了,而且就算没废弃,它也只在组件挂载前执行一次,同样无法处理后续的props更新。
解决方案1:直接用Props渲染(推荐)
其实你完全没必要把props复制到state里,React的数据流就是父组件传props,子组件基于props渲染——props更新时,子组件会自动触发重新渲染。直接用props就能解决你的问题,代码更简洁也更易维护。
修改后的Component.js:
class Component extends React.Component{ renderComponents = () => { // 直接从props取数据,不用存state const { componentsComponents, id } = this.props; // 数据存在时再过滤渲染 if(componentsComponents.length){ return componentsComponents .filter(c => c.inhertedFromId === id) .map(c => { // 注意:给每个子组件加key属性,React渲染优化需要 return <Component key={c.id} name={c.name} id={c.id} componentsComponents={componentsComponents} /> }) } // 没有数据时可以返回加载提示或者null return null; } render(){ return( <div> {/* 加上name方便你看到组件层级 */} <h4>{this.props.name}</h4> {this.renderComponents()} </div> ) } }
为什么这个方案可行?
当App组件的fetch请求完成、setState更新arrayOfComponents后,App会重新渲染,把最新的数组传给子组件的componentsComponents props。子组件的props发生变化,会自动触发重新渲染,renderComponents就会用最新的数组过滤并渲染嵌套组件了。
解决方案2:监听Props变化更新State(特殊场景用)
如果你确实需要把props存到state里(比如要在子组件里修改这份数据),那就要用componentDidUpdate钩子监听props的变化,同步更新state:
class Component extends React.Component{ constructor(props){ super(props); this.state={ componentsComponents: props.componentsComponents || [] } } // 组件更新时(包括props变化)触发 componentDidUpdate(prevProps){ // 对比前后props的数据,如果不一样就更新state if(prevProps.componentsComponents !== this.props.componentsComponents){ this.setState({ componentsComponents: this.props.componentsComponents }) } } renderComponents = () => { if(this.state.componentsComponents.length){ return this.state.componentsComponents .filter(c => c.inhertedFromId === this.props.id) .map(c => { return <Component key={c.id} name={c.name} id={c.id} componentsComponents={this.props.componentsComponents} /> }) } return null; } render(){ return( <div> <h4>{this.props.name}</h4> {this.renderComponents()} </div> ) } }
额外注意点
- 给循环渲染的组件加
key属性:React需要用key来识别每个组件的唯一性,避免渲染错误和性能问题。 - 初始加载状态:你可以在App或者子组件里判断数组长度,显示“加载中...”的提示,提升用户体验。
- 避免不必要的state:React的设计理念是尽量让组件基于props渲染,减少state的使用能让你的组件更纯净、更易调试。
内容的提问来源于stack exchange,提问作者user9686855

