如何在已经调用setState的函数的主体中访问更新后的状态?

时间:2019-06-11 13:28:47

标签: javascript reactjs firebase google-cloud-firestore react-hooks

我目前正在编码一个函数,用于将blogPost对象保存到Firestore数据库中的document中。

预期的例程应为:

  • 单击“保存”按钮并调用savePost()函数
  • savePost()函数应调用uploadImagesAndGetURL()函数,因为在将blogPost的URL保存到Firestore之前,应将src的图像上载到Firebase Storage。 (在我的实际情况下,图片上传是async,我上传的是await,但我认为这里的示例并不有害)。
  • uploadImagesAndGetURL()应该上传图像并将返回的urls存储在src的{​​{1}}数组内的各自的elements属性中。
  • 通过在state数组上运行forEach()并调用elements(setBlogPost)来使用从存储中接收到的新URL更新状态来做到这一点。
  • 然后将调用setState()函数,该函数应该能够访问最新状态,以便能够将saveToFirestore()对象以及图像blogPost保存到数据库中。 / li>

问题

我的问题是,在运行src's函数时,在这种情况下该组件被重新渲染3次(对于3张图像),因为savePost()正在调用{{1} } 3次。每个图像一个。当我到达uploadImagesAndGetURL()功能并登录setBlogPost()状态时,您可以看到只有第一张图像saveToFirestore()发生了变化。

我做对了吗?如何改善此例程,以确保在blogPost之前已更新所有内容?

注意::做到这一点的一种方法是跳过状态更新,并将返回的URL直接传递给src函数,而无需将其存储在状态中。但是,在这种情况下合适的模式是什么?

通过saveToFirestore()函数登录:

saveToFirestore()

下面的片段

saveToFirestore()
{
  "title": "This is the title",
  "body": "Click on the button to upload this post",
  "elements": [
    {
      "type": "image",
      "src": "source0"
    },
    {
      "type": "image",
      "src": null
    },
    {
      "type": "image",
      "src": null
    }
  ]
}

1 个答案:

答案 0 :(得分:1)

当您更改博客状态时,可以在useEffect挂钩中移动保存逻辑,这将触发效果运行。

跟随Edit so.answer.56544887

import React, { useEffect } from "react";
import ReactDOM from "react-dom";

function App() {
  console.log("Rendering App...");
  const [blogPost, setBlogPost] = React.useState({
    title: "This is the title",
    body: "Click on the button to upload this post",
    elements: [
      { type: "image", src: null },
      { type: "image", src: null },
      { type: "image", src: null }
    ]
  });

  function savePost() {
    console.log("Inside savePost");
    uploadImagesAndGetURL();
    // saveToFirestore();
  }

  function uploadImagesAndGetURL() {
    console.log("Inside uploadImages");
    const elements = blogPost.elements.map((_, index) => ({
      type: "image",
      src: `source${index}`
    }));
    setBlogPost(previousPost => ({ ...previousPost, elements }));
  }

  // function uploadImagesAndGetURL() {
  //   console.log("Inside uploadImages");
  //   blogPost.elements.forEach((item, index) => {
  //     console.log("Inside uploadImages - forEach");
  //     setBlogPost(prevState => {
  //       const aux = Array.from(prevState.elements);
  //       aux[index].src = "source" + index;
  //       return {
  //         ...prevState,
  //         elements: aux
  //       };
  //     });
  //   });
  // }

  useEffect(() => {
    function saveToFirestore() {
      console.log("Inside saveToFirestore");
      console.log("Will save the following blogPost to Firestore");
      console.log(blogPost);
    }
    saveToFirestore();
  }, [blogPost]);

  return (
    <React.Fragment>
      <div>{blogPost.title}</div>
      <div>{blogPost.body}</div>
      <div>Image 1 src: {blogPost.elements[0].src}</div>
      <div>Image 2 src: {blogPost.elements[1].src}</div>
      <div>Image 3 src: {blogPost.elements[2].src}</div>
      <button onClick={savePost}>Save Post</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
  

uploadImagesAndGetURL()调用setBlogPost()3次。

而且我实际上还会获得URL列表,并一次性设置elements属性,以防止3次重新呈现。