Next.js表单提交时状态未就绪,如何保证数据库记录完整?
解决方法
核心问题在于你在Promise.all的每个异步回调里单独更新urls状态,而状态更新是异步的,同时then块里捕获的是当前闭包中的urls值(初始空数组),自然拿不到最新的上传结果。另外多次调用setUrls会触发多次重渲染和useEffect,完全没必要。
直接在Promise.all中收集所有上传后的url,一次性处理即可:
const [isPending, startTransition] = useTransition(); const [urls, setUrls] = useState<string[]>([]); const { edgestore } = useEdgeStore(); const onSubmit = async (values: z.infer<typeof formSchema>) => { try { // 用Promise.all直接收集所有上传后的url,不需要逐次更新状态 const uploadedUrls = await Promise.all( filesToAdd.map(async (addedFileState) => { const res = await edgestore.publicFiles.upload({ file: addedFileState.file as File, }); return res.url; // 直接返回url,统一收集 }) ); // 一次性更新状态(可选,如果你后续还需要用到这个状态的话) setUrls(uploadedUrls); // 直接用收集到的uploadedUrls创建记录,不需要等状态更新 startTransition(() => { createRecord({ imagesUrls: uploadedUrls, name: values.name }); }); } catch { toast.error("Something went wrong"); } };
这样做的好处:
- 避免了异步状态更新带来的闭包问题,直接使用同步收集的
uploadedUrls,确保创建数据库记录时拿到完整的url列表 - 只触发一次状态更新(如果需要的话),不会多次调用
setUrls和useEffect - 代码逻辑更清晰,减少不必要的状态更新
如果你确实需要依赖urls状态来触发后续操作(比如上传完成后在页面展示),可以配合useEffect,但只在urls长度和filesToAdd长度一致时执行:
useEffect(() => { // 只有当urls的数量和上传的文件数量一致时,才执行创建记录操作 if (urls.length === filesToAdd.length && urls.length > 0) { startTransition(() => { createRecord({ imagesUrls: urls, name: values.name }); }); } }, [urls, filesToAdd.length, createRecord, values.name, startTransition]);
不过更推荐第一种方案,逻辑更直接,不需要依赖状态的异步更新。
内容的提问来源于stack exchange,提问作者Guillermo Brachetta
相关产品推荐
相关产品推荐

