开发者问题收集

使用 react 定期运行 fetch

2019-12-02
14964

我有一个带有不同反应组件的网格,它们都是独立的 - 它们获取自己的数据并显示它。

我想以某种方式让它们每 15 分钟自动重新获取和更新一次。我的第一个想法是 HOC,但是使用较新的钩子和功能组件,我尝试使用钩子。

所以我已经能够创建一个自定义钩子 - 来处理数据的获取:

const useFetching = (url) => {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState({});
  const [error, setError] = useState(null);
  const [reload, setReload] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);

        if (!response.ok) {
          throw new Error('Something went wrong');
        }

        const json = await response.json();

        if (json) {
          setData(json);
        }
      } catch (error) {
        setError(error);
      }

      setLoading(false);
    }

    fetchData();

  }, [url, reload]);

  return {
    loading,
    data,
    error
  };
};

然后两个组件使用它:

const App1 = () => {
  const {loading, data, error} = useFetching(
    "https://hn.algolia.com/api/v1/search?query=redux"
  );

  if (loading || error) {
    return <p>{loading ? "Loading..." : error.message}</p>;
  }

  return (
    <ul>
      {data.hits.map(hit =>
        <li key={hit.objectID}>
          <a href={hit.url}>{hit.title}</a>
        </li>
      )}
    </ul>
  );
};


const App2 = () => {
  const {loading, data, error} = useFetching(
    "https://hn.algolia.com/api/v1/search?query=balls"
  );

  if (loading || error) {
    return <p>{loading ? "Loading..." : error.message}</p>;
  }

  return (
    <ul>
      {data.hits.map(hit =>
        <li key={hit.objectID}>
          <a href={hit.url}>{hit.title}</a>
        </li>
      )}
    </ul>
  );
};

ReactDOM.render(<><App1 /><App2/></>, document.getElementById('root'));

这很好用 - 使用自定义钩子。所以我在想这样的事情:

设置一个每 15 分钟运行一次的间隔计时器并更改“重新加载”状态标志 - 以触发重新获取和更新。还要确保在组件卸载时取消设置间隔计时器。

要访问状态和 setReload 方法 - 我将计时器放在 useFetching 组件内 - 但这样做不起作用,因为它会启动多个计时器。我想我只有两个组件有 14 个计时器。

我真的不确定如何继续 - 我希望一个计时器只处理所有组件的刷新 - 同时我希望组件独立,而不必与外部计时器共享任何东西。每个组件一个计时器就可以了 - 只要它在组件卸载时正确销毁 - 如果将此机制内置到 useFetching 钩子中就好了。

有没有更好的方法来构建它 - 我没有想到?

非常感谢您的建议!

2个回答

您需要一个 useInterval 钩子。这篇文章详细解释了原因和方法: https://overreacted.io/making-setinterval-declarative-with-react-hooks/

Proustibat
2019-12-02

由于 useFetch 封装了单个组件的获取逻辑,因此每个组件管理自己的间隔是有意义的。您可以通过在挂载时声明一个将在十五分钟后触发的间隔来实现这一点。

useEffect(() =>{
    let interval = setInterval(() => setReload(true), (1000 * 60 * 15))
    //destroy interval on unmount
    return () => clearInterval(interval)
})

有关 useEffect (包括上述效果清理)的更多信息,请参阅 文档

Dupocas
2019-12-02