[英]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: 预期的例程应该是:
savePost()
function savePost()
函数 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
它完成,但我不认为这会伤害这里的例子)。 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
属性中, state
的elements
数组中。 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更新状态来实现。 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
钩子中的保存逻辑,它会触发运行效果。
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.