简体   繁体   English

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

[英]How to access the updated state in the body of a function that has already called setState?

I'm currently coding a function to save the blogPost object into a document in my Firestore database. 我目前正在编写一个函数来将blogPost对象保存到我的Firestore数据库中的document中。

The intended routine should be: 预期的例程应该是:

  • Click on SAVE button and call the savePost() function 单击SAVE按钮并调用savePost()函数
  • The savePost() function should call the uploadImagesAndGetURL() function because the images of the blogPost should be uploaded to Firebase Storage before I save their src URL to the Firestore. savePost()函数应调用uploadImagesAndGetURL()函数,因为在将src URL保存到Firestore之前, blogPost的图像上传到Firebase存储。 (in my real case the image upload is async and I await for it to complete, but I don't think it hurts the example here). (在我的实际情况中,图像上传是async ,我await它完成,但我不认为这会伤害这里的例子)。
  • The uploadImagesAndGetURL() should upload the images and store the returned urls for each one in their respective src property, inside the elements array of the state . uploadImagesAndGetURL()应该上传图像并将返回的urls存储在各自的src属性中, stateelements数组中。
  • It does that by running a forEach() on the elements array and calling setState() (setBlogPost) to update state with the new URL's received from the storage. 它通过在elements数组上运行forEach()并调用setState() (setBlogPost)以使用从存储接收的新URL更新状态来实现。
  • The saveToFirestore() function will then be called and should be able to access the most recent state to be able to save the blogPost object along with the image src's to the database. 然后将调用saveToFirestore()函数,并且应该能够访问最新状态,以便能够将blogPost对象与图像src's一起保存到数据库中。

QUESTION

My problem is that while I'm running the savePost() function the component is being re-rendered 3 times in this case (for 3 images), because the uploadImagesAndGetURL() is calling setBlogPost() 3 times. 我的问题是,当我运行savePost()函数时,在这种情况下组件被重新渲染3次(对于3个图像),因为uploadImagesAndGetURL()正在调用setBlogPost() 3次。 One for each image. 每个图像一个。 And when I reach the saveToFirestore() function and I log the blogPost state, you can see that only the first image src has changed. 当我到达saveToFirestore()函数并记录blogPost状态时,您可以看到只有第一个图像src已更改。

Am I doing it right? 我做得对吗? How can I improve this routine to make sure everything has been updated before I saveToFirestore() ? saveToFirestore()之前,如何改进此例程以确保所有内容都已更新?

NOTE: One way to do it would be to skip the state update and pass the returned URLs straight to the saveToFirestore() function without ever storing them on the state. 注意:一种方法是跳过状态更新并将返回的URL直接传递给saveToFirestore()函数,而不将它们存储在状态中。 But, what is the appropriate pattern in this situation? 但是,在这种情况下,适当的模式是什么?

Log from the saveToFirestore() function: 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
    }
  ]
}

SNIPPET BELOW 下面是SNIPPET

 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'); 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 }); }); }); } function saveToFirestore() { console.log('Inside saveToFirestore'); console.log('Will save the following blogPost to Firestore'); console.log(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')); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script> <div id="root"></div> 

You can move the save logic within the useEffect hook as you are changing the blog state, it'd trigger the effect to run. 您可以在更改博客状态时移动useEffect钩子中的保存逻辑,它会触发运行效果。

Follow along on 继续 编辑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"));

the uploadImagesAndGetURL() is calling setBlogPost() 3 times. uploadImagesAndGetURL()调用setBlogPost()3次。

And also I'd actually get the list of URLs and set the elements property in one go to prevent re-render 3 times. 而且我实际上得到了URL列表并一次设置了elements属性以防止重新渲染3次。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在调用setState之前,状态会更新 - state gets updated before setState is called 如何在使用反应挂钩的 setState 方法之后访问更新的 state? - How to access the updated state after setState method with react hooks? function 不会使用更新的 state 如果 setState function 在 react 开始执行后被调用 - function wont use the updated state if the setState function was called after execution began in react 反应 function 组件的 setState 未更新我的 state - setState for react function component not updated my state 当从 tinyMCE onAction 方法调用 setState 时,State 未更新 - State not updated when setState is called from tinyMCE onAction method 如何在 setState 回调中获取更新的状态? - How to get the updated state in setState callback? 当父项的状态必须由子项更新时,this.setState不起作用 - this.setState not working when a parent's state has to be updated by a child 调用 setState 后 React 如何分离旧状态和新状态? - How does React separate old and new state after setState has been called? 每次更新 state 时都会调用随机播放 function 吗? - shuffle function called every time state is updated? 当消费者调用时,作为上下文值传递的函数无法访问更新的组件状态 - Function passed as context value can't access updated component state when called by a consumer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM