你面临的问题是批量处理.
什么是配料?
React.js自版本18以来提供了一个名为批处理的新概念.这意味着它将某些状态更新推迟到下一次重新呈现以避免不必要的重新呈现.
在事件处理程序中自动react 批处理状态(如您的 case )和setTimeout
回调.简而言之,当您在事件处理程序(onClick
、onSubmit
等)中设置状态时,在事件处理程序结束之前,Reaction不会重新呈现(更新状态).
示例
const [state, setState] = useState(1);
const onSubmit = async (values: ValuesType) => {
await doSomething(values);
// Currently, state === 1
setState(2)
// Still, state === 1, not 2
}
阅读有关Reaction中批处理的更多信息:
你的例子怎么样?
const onSubmit = async (values: z.infer<typeof formSchema>) => {
try {
await Promise.all(
filesToAdd.map(async (addedFileState) => {
const res = await edgestore.publicFiles.upload({
file: addedFileState.file as File,
});
setUrls((prev) => [...prev, res.url]);
}),
)
.then(() => {
startTransition(() => {
createRecord({ imagesUrls: urls, name: values.name })
});
})
} catch {
toast.error("Something went wrong");
}
};
下一行是在事件处理程序和for循环中:
setUrls((prev) => [...prev, res.url]);
如果React重新呈现组件,它将销毁整个事件处理程序.设想每次重新呈现都会顺利地上载第一个文件,然后当它到达前一行时,Reaction重新呈现组件,并从头开始执行事件处理程序,直到它到达同一行,然后重新呈现,依此类推……这将是一个无限循环.
相反,它会做什么呢?
让我们简化一下您的函数
const [numbers, setNumbers] = useState<number[]>([])
const onSubmit = () => {
// here, numbers = []
setNumbers(nums => [...nums, 1])
// At the moment, numbers still equal = []
// But React remembers that when it finish the event handler
// It will run this function `nums => [...nums, 1]` on the numbers state
setNumbers(nums => [...nums, 2])
// What do you think `numbers` equal?
// Yes, numbers = [], and react will re-rember to run this function
// nums => [...nums, 2]
console.log(numbers);
// What is the output?
// empty array.
}
在此事件处理程序完成删除后,Reaction将在下一次重新呈现中运行此操作
let numbers = [];
const batchesUpdates = [
function (nums) { return [...nums, 1] },
function (nums) { return [...nums, 2] },
]
batchesUpdates.forEatch(func => {
numbers = func(numbers)
})
明白了?
如何处理您的用例?
- 可以在事件处理程序中声明内部变量,并使用它
const onSubmit = async (values: z.infer<typeof formSchema>) => {
// internal variable
const updloadedFilesUrls = [];
try {
await Promise.all(
filesToAdd.map(async (addedFileState) => {
const res = await edgestore.publicFiles.upload({
file: addedFileState.file as File,
});
// update the internal variable
updloadedFilesUrls.push(res.url)
}),
)
.then(() => {
startTransition(() => {
// use the internal variable
createRecord({ imagesUrls: updloadedFilesUrls, name: values.name })
});
// Update the state
setUrls(urls => [...urls, ...updloadedFilesUrls])
})
} catch {
toast.error("Something went wrong");
}
};
高级: Select 加入/退出批处理
要 Select 退出自动批处理,您可以使用flushSync
import { flushSync } from "react-dom";
const onSubmit = (event) => {
doSomeLogic()
flushSync(() => { setProgressBar(w => w++) })
doSomeOtherLogic()
}
为既不是事件处理程序也不是超时回调的函数手动批处理订阅unstable_batchedUpdates
import { unstable_batchedUpdates as batchedUpdates } from "react-dom";
const doLogic = batchedUpdates(() => { /* batched */ })