【问题标题】:Where to set state after making an async call?进行异步调用后在哪里设置状态?
【发布时间】:2018-01-21 01:38:49
【问题描述】:

我正在尝试从 dynamo db 中提取一些数据,将其存储在我的本地状态中,然后对其进行处理。我可以提取数据,我认为我可能需要处理异步的东西,所以我使用异步等待

async componentDidMount() {
//aws secret key stuff here
const ddb = new AWS.DynamoDB({ apiVersion: '2012-10-08' });
    const res = await ddb.scan(params, (err, data) => {
      if (err) {
        console.log(err, err.stack);
      } else {
        console.log(data);
      }
    });
    this.setState({ products: data })
}

我有几个问题。我基本上是想等到扫描完成,这样我就知道数据已经成功拉取,然后我可以设置状态。

首先,linter 抱怨说我不应该在 componentDidMount 中设置状态,那么我应该在哪里设置呢?

其次,在使用数据之前等待某些请求完成的正确方法是什么?

我在this.setState({products: data}) 的地方做了一个console.log(data),它记录了正确的事情。

但是当我尝试像这样迭代我的数据时:

{this.state.products.Items.map((product) => (

我无法读取未定义的地图??

【问题讨论】:

    标签: javascript reactjs asynchronous async-await amazon-dynamodb


    【解决方案1】:

    在 componentDidMount 中设置状态会导致额外的渲染。 (reference)

    在这个方法中调用 setState() 会触发额外的渲染,但是 它会在浏览器更新屏幕之前发生。这保证 即使在这种情况下 render() 将被调用两次, 用户不会看到中间状态。谨慎使用此模式 因为它经常会导致性能问题。然而,它可以是 当您需要测量时,对于模态和工具提示等情况是必需的 渲染之前的 DOM 节点,这取决于它的大小或 位置。

    在某些情况下,您必须在 componentDidMount 上调用一些 API 和 setState。

    我看到你需要改变的两件事。似乎 ddb.scan 将回调作为第二个参数,因此您不需要 Promises 的 ascyn/await。

    在这种情况下,您需要在回调中使用 setState。

    ddb.scan(params, (err, data) => {
      if (err) {
        console.log(err, err.stack);
      } else {
        console.log(data);
        this.setState({ products: data });
      }
    });
    

    如果在其他应用中你使用 Promises,你可以使用 async/wait 并在 await 之后使用 setState。

    const res = await promisecall();
    this.setState(res.stuff);
    

    在访问您的产品数组之前,您应该检查它是否已经存在。

    this.state.products.Items
    

    在第一次渲染时未定义。 componentDidMount 运行并设置它但是

    this.state.products.Items.map
    

    已经抛出错误。

    做类似的事情

    this.state.products && 
    this.state.products.Items &&
    this.state.products.Items.length > 0 &&
    this.state.products.Items.map(etc...)
    

    只有在 this.state.products.Items 存在时才会运行。这样做是为了防止第一次渲染(在 componentDidMount 中设置状态之前)失败。

    【讨论】:

    • 当我在回调中设置状态时,它在渲染方法中的 map() 上失败了,因为它不知道 Items 是什么
    • @TheWalrus 我添加了更多关于正在发生的事情的解释
    • 好的,谢谢。你能解释一下吗...当我在回调中设置状态然后立即记录下面的状态时,它就在那里。但是当我在 render 方法中记录状态时, products 是一个空数组, products.Items 是未定义的。所以它正在设置但不持久或在渲染功能中不可用......
    • @TheWalrus 因为当您还没有调用任何 API 或任何东西时,该日志会在第一次渲染时发生。就像我在 componentDidMount 中的答案设置状态中所说的那样会导致再次渲染。所以你必须做一些检查才能让第一个渲染通过。
    • 好的,这基本上可以工作。它只打印出一种产品,但我相信这一定是与当前产品不同的问题。所以我会认为这已解决。非常感谢!
    【解决方案2】:

    首先:您可以从 componentDidMount 调用异步函数,而不是使 componentDidMount 异步,从而消除您的 linting 问题

    第二:您不需要使用 ddb.scan 进行等待,因为它会在回调中返回响应。你可以在里面设置状态,

    第三:由于在渲染后调用 componentDidMount,您的初始状态可能为 null/未定义,因此您在渲染方法中遇到错误。确保将状态初始化为空数组或执行验证

    componentDidMount() {
        this.dynamoDBScan();
    }
    
    async dynamoDBScan() {
         //aws secret key stuff here
        const ddb = new AWS.DynamoDB({ apiVersion: '2012-10-08' });
        const res = await ddb.scan(params, (err, data) => {
          if (err) {
            console.log(err, err.stack);
          } else {
            this.setState({ products: data })
          }
        });
    
    
    }
    

    然后在渲染中

    {
       this.state.products && 
       this.state.products.Items.length > 0 &&
       this.state.products.Items.map((product) => (
    

    【讨论】:

    • 好的,但这只是导致它不会爆炸,那我怎样才能在页面上获得产品?
    • 没有在回调中放置 setState 调用,在渲染方法中显示结果,您可以尝试在渲染中使用console.log(this.state.products)
    • 我已经完成了,它返回了初始状态的空数组,这就是我感到困惑的原因。因为我认为回调必须首先执行?
    猜你喜欢
    • 1970-01-01
    • 2016-12-24
    • 1970-01-01
    • 2014-09-23
    • 2018-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多