【问题标题】:How to deal with asynchronous props and state如何处理异步 props 和 state
【发布时间】:2020-10-05 09:36:22
【问题描述】:

我无法完全理解这一点。

我必须传递异步获取的数据。问题是,道具也是异步的。这是该组件的简化版本:

import React, { Component }  from 'react'
import CSVLink from 'react-csv'
import generateData from './customApi/generateData

type Props = { job?: JobType | undefined }
type State = { csvData: string[][] }

class MyComponent extends Component<Props, State> {
  state = {
    csvData = [],
  }

  generateCsv = async (jobId: string) => {
    const csvData = await generateData(jobId)
    this.setState({ csvData })
  }

  async componentDidMount() {
    const { job } = this.props
    await this.generateCsv(job.id)
  }

  render() {
    const { csvData } = this.state

    return (
       <CSVLink data={csvData}>
          <p>Download csv</p>
       </CSVLink>
    )
  }
}

export default connectFirestore(
   (db, params) => ({ getJob(db, params.id) })
)

基本上,我的道具是从对 firestore 的 API 调用中获取的,因此加载作业需要一段时间。问题是,当我尝试在async componentDidMount() 中传递jobId 时,它最终会传递undefined,因为尚未加载job props

我没有将整个传递状态链接到正在进行的异步调用业务,但我想不出任何其他方式,我如何将 csvDatagenerateDate() 异步调用仅传递一次 @987654329 @ 已解决。

我想解决这个问题的最简单方法是,可能以某种方式检查componentDidMount() 中的道具是否已经被获取?

解决这个问题的正确方法是什么?

【问题讨论】:

    标签: javascript reactjs typescript asynchronous react-lifecycle


    【解决方案1】:

    您缺少实现设置道具的构造函数

    constructor(props){
      super(props);
      this.state = {
         csvData = [],
      };
    }
    
    componentDidMount(){
      //This will work
      console.log(this.props.job);
    };
    

    【讨论】:

    • 虽然我同意不应该省略构造函数,但在初始渲染时这仍会将props.job 保留为undefined
    【解决方案2】:

    如果job 属性应该是异步的,请异步执行:而不是传递一个将来会更改的值,您可以传递一个Promise,它使用id 解析,而不是更改您的componentDidMount,如下所示:

    componentDidMount() {
        const id = await this.props.job;
        this.generateCsv(id)
    }
    

    希望这会有所帮助。

    【讨论】:

    • 你不能在同步的componentDidMount()中调用await,await this.props.job也不会做任何事情,因为它不是一个函数。
    【解决方案3】:

    也许您应该更改父组件中的代码,我想您正在那里进行 API 调用,因为您将这些数据作为您展示给我们的组件中的道具传递。

    我还想象您正在使用 fetch 命令进行 API 调用,该命令可以在 API 调用完成加载时触发 .then(()=>{}) 方法,之后您可以更改该方法的状态携带 API 的组件获取数据,然后渲染这个孩子。我最近在我的项目中使用的东西是从 API 加载、更新状态并有条件地渲染子组件,它不会知道我进行了 API 调用,因为我正在传递已经加载的数据。通常在等待的时候我会放这样的东西:

    if(this.state.dataFetched == null)
       return(<h1>Loading page...</h1>)
    else return <childComponent loadedData = {this.state.dataFetched}/>
    

    然后以 this.props.loadedData 的形式访问该数据

    【讨论】:

      【解决方案4】:

      不确定这是否是最佳解决方案,但它确实有效。

      我决定使用componentDidUpdate() 生命周期方法,在这里我比较道具是否已经更新,一旦更新,我将调用异步函数来生成 csv 数据。

      async componentDidUpdate(prevProps: Props) {
        if (prevProps !== this.props && !!this.props) {
          const { job } = this.props
          if (job) {
            await this.generateCsv(job.id)
          }
        }
      }
      

      这样,每次props.job 中的数据发生变化时,我们都会生成新数据,而且我们不会尝试在 undefined 上调用 generateCsv(),而它仍在从 firestore 中获取。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-10
        • 2017-11-20
        • 2022-07-07
        • 2021-07-15
        • 1970-01-01
        • 2014-11-05
        • 2014-04-10
        • 2018-07-04
        相关资源
        最近更新 更多