在 React 中等待 useState

await useState in React

我已经和这个代码斗争了好几天了,但我还是没弄好。

问题: 我正在使用具有拖放区的表单。在提交处理程序中,我需要将图像的 url 保存在一个数组中,但它总是作为一个空数组返回。

正在声明图像数组:

const [images, setImages] = useState([]);

在这里,我获取了图像的 url 并尝试将它们保存在数组中:

const handleSubmit = () => {

      files.forEach(async(file)=> {
          const bodyFormData = new FormData();
          bodyFormData.append('image', file);
          setLoadingUpload(true);
          try {
            const { data } = await Axios.post('/api/uploads', bodyFormData, {
              headers: {
                'Content-Type': 'multipart/form-data',
                Authorization: `Bearer ${userInfo.token}`,
              },
            });
            setImages([...images,data])
            setLoadingUpload(false);
          } catch (error) {
            setErrorUpload(error.message);
            setLoadingUpload(false);
          }
      })
  }

这里有 submitHandler 函数,我在其中调用 handleSubmit():

const submitHandler = (e) => {

    e.preventDefault();
    handleSubmit();
   dispatch(
        createCard(
          name,
          images,
        )
      );
}

我知道这是因为它执行代码的顺序,但我找不到解决方案。 非常感谢您!!!!

为了防止竞争条件,您可以尝试使用 setImages 和当前值,如下所示:

setImages(currentImages => [...currentImages, data])

这样,您将准确使用您所在州当前包含的内容,因为在这种情况下 images 可能不正确。

作为另一个提示,我建议您 map 文件,而不是循环遍历您的文件,如 files.map(...。有了这个,您可以将所有文件条目映射到一个承诺,最后,将它们合并到一个包含所有请求的承诺。所以你可以简单地看一下。

问题

React 状态更新是异步处理的,但状态更新函数本身不是 async 所以你不能等待更新发生。您只能从当前渲染周期访问状态值。这就是为什么 images 可能仍然是您的初始状态,一个空数组 ([])。

const submitHandler = (e) => {
  e.preventDefault();
  handleSubmit(); // <-- enqueues state update for next render
  dispatch(
    createCard(
      name,
      images, // <-- still state from current render cycle
    )
  );
}

解决方案

我认为您应该重新考虑如何计算 images 的下一个状态,进行一次更新,然后使用 useEffect 挂钩使用更新后的状态值调度操作。

const handleSubmit = async () => {
  setLoadingUpload(true);
  try {
    const imagesData = await Promise.all(files.map(file => {
      const bodyFormData = new FormData();
      bodyFormData.append('image', file);
      return Axios.post('/api/uploads', bodyFormData, {
        headers: {
          'Content-Type': 'multipart/form-data',
          Authorization: `Bearer ${userInfo.token}`,
        },
      });
    }));
    setImages(images => [...images, ...imagesData]);
  } catch(error) {
    setErrorUpload(error.message);
  } finally {
    setLoadingUpload(false);
  }
}

const submitHandler = (e) => {
  e.preventDefault();
  handleSubmit();
}

React.useEffect(() => {
  images.length && name && dispatch(createCard(name, images));
}, [images, name]);