如何使用反应路由器基于状态导航

How to navigate based on state using react router

在我的 React 项目中,我使用 fetch api 从后端获取用户配置文件。如果 API 调用中出现任何错误,我会在屏幕上显示它。

const navigate = useNavigate();
const [errorMessage, setErrorMessage] = React.useState("");

...

const handleGetProfile = async () => {
  await fetch(`${API_URL}/profile`).then(...).catch(err=>setErrorMessage(err.message))
  !errorMessage && navigate("/");
}

只有在 api 调用中 没有错误 时,我才想导航到根路径。所以我正在检查错误是否为空并导航到根路径。

这种方法的问题是 setErrorMessage 不能保证立即更新,因为它会安排状态更新,所以它总是导航到根路径,即使出现错误也是如此。

我该如何解决这个问题,有什么建议吗?

正确,因为 React 状态更新是异步处理的,并被视为 const errorMessage 状态不会在 handleGetProfile 回调中更新。

const handleGetProfile = async () => {
  await fetch(`${API_URL}/profile`)
    .then(...)
    .catch(err => setErrorMessage(err.message));
  !errorMessage && navigate("/");
}

async/await 与 Promise 链混合使用也是 anti-pattern。通常你使用其中之一。

要解决,您应该将 navigate 调用 移动到 逻辑的“已解决”部分。由于 fetch returns Promise 和 only reject on network errors 你还需要检查响应状态。

Checking that the fetch was successful

A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side, although this usually means permission issues or similar — a 404 does not constitute a network error, for example. An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. The code would look something like this:

fetch('flowers.jpg')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not OK');
    }
    return response.blob();
  })
  .then(myBlob => {
    myImage.src = URL.createObjectURL(myBlob);
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });
  • 使用 Promise 链

    const handleGetProfile = () => {
      fetch(`${API_URL}/profile`)
        .then((response) => {
          if (!response.ok) {
            throw new Error('Network response was not OK');
          }
          // handle any successful response stuff
          navigate("/");
        })
        .catch(err => {
          setErrorMessage(err.message || err);
        });
    }
    
  • async/awaittry/catch

    结合使用
    const handleGetProfile = async () => {
      try {
        const response = await fetch(`${API_URL}/profile`);
        if (!response.ok) {
          throw new Error('Network response was not OK');
        }
        // handle any successful response stuff
        navigate("/");
      } catch(err) {
        setErrorMessage(err.message || err);
      }
    }
    
  • 使用 useEffect 挂钩来响应状态变化

    const navigate = useNavigate();
    const [isFetched, setIsFetched] = React.useState(false);
    const [errorMessage, setErrorMessage] = React.useState("");
    
    useEffect(() => {
      if (isFetched && !errorMessage) {
        navigate("/");
      }
    }, [errorMessage, isFetched, navigate]);
    
    ...
    
    const handleGetProfile = async () => {
      setErrorMessage(null);
      setIsFetched(false);
      try {
        const response = await fetch(`${API_URL}/profile`);
        if (!response.ok) {
          throw new Error('Network response was not OK');
        }
        // handle any successful response stuff
        navigate("/");
      } catch(err) {
        setErrorMessage(err.message || err);
      } finally {
        setIsFetched(true);
      }
    }