如何在render()执行前完成componentWillMount()中的异步调用?
解决React类组件中异步数据加载前避免render显示null的问题
首先得明确一点:React的render()方法是同步执行的,没办法直接让它等待componentWillMount里的异步操作完成——而且componentWillMount在React 16.3之后已经被标记为不安全的生命周期方法,官方更推荐用componentDidMount来处理异步数据请求。
那怎么实现你想要的「确保异步完成后再显示目标内容」呢?核心思路是通过状态管理加载状态,分阶段渲染:
方案1:添加加载状态,分阶段渲染
在组件的state里新增一个loading字段,用来标记异步请求是否完成。初始时loading设为true,异步请求完成后再把它设为false,同时更新foo的数据。这样render的时候就可以根据loading状态来显示不同内容:
interface State { foo?: AClass; loading: boolean; // 新增加载状态 } export class BClass extends React.Component<Props, State> { // 初始化state,loading默认true state = { foo: undefined, loading: true }; // 用componentDidMount替代componentWillMount async componentDidMount(){ await this.run(); } async run(){ let a = await bar(); // 请求完成后更新foo,同时把loading设为false this.setState({foo: a, loading: false}); } render() { // 如果还在加载,显示加载提示(比如"加载中...") if (this.state.loading) { return ( <View> <Text>加载中...</Text> </View> ); } // 加载完成后再渲染目标内容 return ( <View> <Text> {this.state.foo?.getText() || "null"} </Text> </View> ); } }
方案2:在父组件中提前获取数据
如果你的业务场景允许,也可以把异步请求放到父组件里完成,等数据拿到后再渲染BClass组件。这样BClass直接接收父组件传递的foo作为props,就不用自己处理异步和加载状态了:
// 父组件示例 export class ParentComponent extends React.Component { state = { foo: undefined, loading: true }; async componentDidMount() { const a = await bar(); this.setState({foo: a, loading: false}); } render() { if (this.state.loading) { return <Text>加载中...</Text>; } // 数据就绪后再渲染BClass return <BClass foo={this.state.foo} />; } } // 修改BClass,直接使用props的foo interface BProps { foo: AClass; } export class BClass extends React.Component<BProps> { render() { return ( <View> <Text>{this.props.foo.getText()}</Text> </View> ); } }
为什么不能让render等待异步?
React的设计理念是UI是状态的映射,render()会在状态更新时自动触发。如果强行让render等待异步操作,会导致组件长时间处于未渲染状态,阻塞用户界面,影响体验。通过加载状态来过渡,才是符合React设计模式的正确做法。
内容的提问来源于stack exchange,提问作者Grey Haven
相关产品推荐
相关产品推荐

