React中调用API前如何确认setState已生效?上传图片后提交Mongo数组为空
问题根源
- 闭包旧值问题:你在
handleSubmit触发时就把当前的form状态快照传给了postData,后续调用setForm更新的是组件的最新状态,不会改动已经传入postData的旧form对象。你在控制台看到对象有值是因为浏览器控制台打印引用类型时会动态读取最新值,实际发送请求时用的还是最初传入的带空数组的旧版本。 - 字段名不匹配:初始化
form时定义的图片存储字段是photoURLs,但在handleUpload里更新状态时用的字段名是photos,两者不一致。 - setState异步问题:React的状态更新是异步操作,就算绕过闭包问题,
setForm触发的状态更新也不会在当前同步代码块里立刻生效,你在Promise.all的回调里拿到的还是旧的状态值。
解决方法
不需要在上传每个文件的时候单独更新状态,直接在所有文件上传完成后收集所有URL,当场组装提交数据即可,完全绕开异步状态更新的问题,保证提交的数据是实时生成的最新值。
修正后的代码如下:
const [form, setForm] = useState({ userId: '', post: '', createdAt: createdAt, photoURLs: [], }) const handleSubmit = (e) => { e.preventDefault() newPost ? postData() : ... } // 上传文件到Firebase,直接返回下载URL,不操作外部状态 async function handleUpload(file) { const storageRef = useStorage.ref("PostImages"); const fileRef = storageRef.child(`${nanoid()}`); await fileRef.put(file) return fileRef.getDownloadURL() } // 提交数据函数 const postData = async () => { setLoading(true) try { const thisFileArray = fileInput.current.files; const uploadTasks = []; for (let i = 0; i < thisFileArray.length; i++) { uploadTasks.push(handleUpload(thisFileArray[i])); } // 等待所有上传完成,同步拿到所有url数组 const photoURLs = await Promise.all(uploadTasks) // 当场组装提交数据,用最新form状态+刚拿到的url,没有异步问题 const submitData = { ...form, photoURLs: photoURLs } // 此处打印submitData.photoURLs的值就是实际提交的值,不会有前后不一致的问题 const response = await axios.post('/api/posts', submitData) // 如果需要更新本地状态再调用setForm setForm(prev => ({...prev, photoURLs})) // 后续成功逻辑 ... } catch (error) { // 错误处理逻辑 ... } finally { setLoading(false) } }
内容的提问来源于stack exchange,提问作者Ray Purchase
相关产品推荐
相关产品推荐

