首页 > 解决方案 > 在反应中获取数据的最佳方法是什么?

问题描述

我目前使用 contentful 进行数据管理,所以这次不是常用的 axios/fetch 方法。

我正在使用 useContext 将数据共享给我的组件,并使用 useEffect 设置新数据的状态。你问什么问题?丑陋的语法。当我在第二次重新渲染中传递数据时,我无法立即访问数据,data[0][0]它还不存在,因此会引发错误。这导致了这种令人作呕的语法:<h5>{data ? data[0].sys.contentType.sys.id : ""}</h5>它可能“很好”,并且“有效”。但这对我来说绝对是残酷的。

应用程序.jsx

const App = () => {
  const contentfulHook = useState(null);

  
  useEffect((e) => {
    client.getEntries().then((res) => contentfulHook[1](res.items));

/*
    OR - same result
    (async () => {
      const data = await client.getEntries().then((res) => res.items);
      contentfulHook[1](await data);
    })();
*/
 //Remove preloader when content is loaded
    setTimeout(() => {
      const preloader = document.getElementById("preloader");
      preloader.style.opacity = 0;
      preloader.addEventListener("transitionend", (e) => {
        preloader.remove();
      });
    }, 0);
  }, []);
  console.log(contentfulHook[0]);

  return (
    <contentfulContext.Provider value={contentfulHook}>
      <BrowserRouter>
        <Global />
        <Pages />
      </BrowserRouter>
    </contentfulContext.Provider>
  );
};

render(<App />, document.getElementById("root"));

标签: javascriptreactjs

解决方案


I'd suggest two things on that matter:

  1. Encapsulate that request logic in a custom hook, so you can remove that code from your component.
  2. Use data, loading and error pattern (as used by Apollo and probably other libraries).

The result would be something like:

const App = () => {
  const { data, loading, error } = useRequest();

  return (
    <contentfulContext.Provider value={data}>
      <BrowserRouter>
        <Global />
        <Pages />
      </BrowserRouter>
    </contentfulContext.Provider>
  );
};

const useRequest = () => {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)

  useEffect(() => {
    setLoading(true)
    client.getEntries()
      .then((res) => setData(res.items))
      .catch((e) => setError(e))
      .finally(() => {
        removePreloader();
        setLoading(false);
      });
  }, []);

  return { data, loading, error };
}

const removePreloader = () => {
  setTimeout(() => {
    const preloader = document.getElementById("preloader");
    preloader.style.opacity = 0;
    preloader.addEventListener("transitionend", (e) => {
      preloader.remove();
    });
  }, 0);
}

render(<App />, document.getElementById("root"));

Either way, you still need to check for the data before accessing the data attributes when rendering. Even if it is on a top-level condition like:

const App = () => {
  const { data, loading, error } = useRequest();

  return (
    <contentfulContext.Provider value={data}>
      <BrowserRouter>
        <Global />
        <Pages />
        {data && <h5>{data[0].sys.contentType}</h5>}
      </BrowserRouter>
    </contentfulContext.Provider>
  );
};

推荐阅读