【发布时间】:2019-10-25 22:42:12
【问题描述】:
我目前正在编写一个函数,用于将 blogPost 对象保存到我的 Firestore 数据库中的 document 中。
预期的例程应该是:
- 点击保存按钮并调用
savePost()函数 -
savePost()函数应该调用uploadImagesAndGetURL()函数,因为在我将srcURL 保存到 Firestore 之前,应该将blogPost的图像上传到 Firebase 存储。 (在我的真实情况下,图片上传是async和我await来完成,但我认为这不会影响这里的示例)。 -
uploadImagesAndGetURL()应该上传图像并将返回的urls存储在它们各自的src属性中,在state的elements数组中。 - 它通过在
elements数组上运行forEach()并调用setState()(setBlogPost) 以使用从存储中接收到的新 URL 来更新状态。 - 然后将调用
saveToFirestore()函数,并且应该能够访问最新状态,以便能够将blogPost对象与图像src's一起保存到数据库中。
问题
我的问题是,当我运行 savePost() 函数时,在这种情况下组件被重新渲染 3 次(对于 3 张图像),因为 uploadImagesAndGetURL() 正在调用 setBlogPost() 3 次。每个图像一个。当我到达saveToFirestore() 函数并记录blogPost 状态时,您可以看到只有第一张图像src 发生了变化。
我做得对吗?我如何改进此例程以确保在saveToFirestore() 之前所有内容都已更新?
注意: 一种方法是跳过状态更新并将返回的 URL 直接传递给 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
}
]
}
以下片段
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>
【问题讨论】:
-
为什么不用效果钩子来触发saveToFirestore?
-
效果会基于什么运行?因为我有多个
setState()电话?我可以创建一个带有hasUploaded布尔值的状态标志并基于它运行效果。但即使我在src的所有setState之后更新了这个布尔值,我可以确定这些 setState 调用的顺序并且hasUploaded将是最后一个应用吗? -
React 批量调用
setState,因为它发生在事件处理程序中,所以效果挂钩只会在整个更新后运行一次。 -
@Remeus 即使
setState调用在事件处理程序内部更深一层,批处理也会工作吗?就像我的情况一样,处理程序是savePost,它调用uploadImagesAndGetURL,然后它调用setState? -
只要 React 可以确定何时会进行所有状态更改(例如没有异步),就应该是的。
标签: javascript reactjs firebase google-cloud-firestore react-hooks