首页 > 解决方案 > 在异步函数中使用 React useState 钩子更新函数

问题描述

尝试 setState 似乎不会立即更新状态,我不确定如何达到预期的结果。

  const Settings = (props: Props) => {
      const { user } = props;
      const initState: State = {
        newName: user.displayName,
        newAvatarUrl: user.avatarUrl,
        disabled: false,
      };
      const [form, setForm] = useState(initState);

  const handleUploadFile = async () => {
    const fileElement = document.getElementById('upload-file') as HTMLFormElement;
    const file = fileElement.files[0];

    if (file == null) {
      return;
    }
    setForm({ ...form, disabled: true });

    try {
      const response = await asynFunc1()

      await asyncFun2(response)

      setForm({ ...form, newAvatarUrl: response.url });

      await asyncFunc3(form.newAvatarUrl) // Expected: response.url Observed: old value
    } catch (error) {
      consol.log(error)
    } finally {
      setForm({ ...form, disabled: false });
    }
  };

  return (
        <h4>Your Avatar</h4>
        <Avatar
          src={form.newAvatarUrl}
          style={{
            display: 'inline-flex',
            verticalAlign: 'middle',
            marginRight: 20,
            width: 60,
            height: 60,
          }}
        />
        <label htmlFor="upload-file">
          <Button variant="outlined" color="primary" component="span" disabled={form.disabled}>
            Update Avatar
          </Button>
        </label>
        <input
          accept="image/*"
          name="upload-file"
          id="upload-file"
          type="file"
          style={{ display: 'none' }}
          onChange={handleUploadFile}
        />
  );
};

看起来 setForm 不是同步的,但它不返回承诺,所以我不确定如何以同步方式使用它。

标签: reactjsreact-hooks

解决方案


正如评论中提到的,状态更新是异步的。相反,您应该将 response.url 传递给 asyncFunc3 调用,并在成功返回后设置您的表单状态。会是这样的:

const Settings = (props: Props) => {
  const { user } = props;
  const initState: State = {
    newName: user.displayName,
    newAvatarUrl: user.avatarUrl,
    disabled: false,
  };
  const [form, setForm] = useState(initState);

  const handleUploadFile = async () => {
    const fileElement = document.getElementById('upload-file') as HTMLFormElement;
    const file = fileElement.files[0];

    if (file == null) {
      return;
    }
    setForm({ ...form, disabled: true });

    try {
      const response = await asynFunc1()

      await asyncFun2(response)

      await asyncFunc3(response.url)
      
      setForm({ ...form, newAvatarUrl: response.url });
    } catch (error) {
      consol.log(error)
    } finally {
      setForm({ ...form, disabled: false });
    }
  };

  return (
        <h4>Your Avatar</h4>
        <Avatar
          src={form.newAvatarUrl}
          style={{
            display: 'inline-flex',
            verticalAlign: 'middle',
            marginRight: 20,
            width: 60,
            height: 60,
          }}
        />
        <label htmlFor="upload-file">
          <Button variant="outlined" color="primary" component="span" disabled={form.disabled}>
            Update Avatar
          </Button>
        </label>
        <input
          accept="image/*"
          name="upload-file"
          id="upload-file"
          type="file"
          style={{ display: 'none' }}
          onChange={handleUploadFile}
        />
  );
};

推荐阅读