【问题标题】:Attempting to access data after fetching with custom hook not working使用自定义钩子获取后尝试访问数据不起作用
【发布时间】:2022-01-08 19:19:06
【问题描述】:

我有一个基本的 useFetch 钩子实现,它定义了一个 fetchData 函数,如果成功,它将 setData 到一些 JSON,然后我会在没有依赖关系的 useEffect 上调用它,并且钩子返回有状态的数据值。我发现这并不理想,因为我想根据事件动态获取内容。

因此,我改为将 useFetch 挂钩更改为简单地将 fetchData 函数引用与数据一起返回,并且不再在挂钩内调用 fetchData。

const useFetch = () => {
  const [data, setData] = useState([]);
  const fetchData = async (url) => {
    try {
      const response = await fetch(url);
      if (response.ok) {
        const jsonData = await response.json();
        setData(jsonData);
      } else {
        throw new Error(response.status);
      }
    } catch (err) {
      console.error(err);
    }
  };
  return { fetchData, data };
};

然而,这在我使用这个钩子的地方引入了问题。我以前从未使用过这种模式,所以我真的不知道我在做什么,但是在调用 fetch 函数后我无法对数据值做任何事情。这基本上是我在另一个功能组件中使用钩子的方式:

  const [displayedItems, setDisplayedItems] = useState([]);

  const { fetchData, data } = useFetch();

  useEffect(() => {
    fetchData(urlGoesHere);
  }, []);

  useEffect(() => {
    setDisplayedItems(data);
    console.log(displayedItems);
  }, [data]);

这很丑陋,而且不起作用。我尝试将它们全部放在一个 useEffect 中,但这也不起作用。有时,当我在 CRA 中重新加载时,我可以看到正在记录的数据,但通常当我的页面加载时,数据只是保持未定义。所以我基本上通过改变 useFetch 来创建另一个问题,而不是实际使用 fetch 函数(它的缺点是无法在我的常规函数​​和事件回调中调用),现在我似乎什至无法渲染任何东西。

我对 React 和异步的东西还很陌生,所以我很感激能考虑到这一点的回应。如果我正在尝试做的事情有更好的模式,我很想听听,但我想把它放在香草反应地里,所以没有图书馆,等等。再一次,我的原因'返回对 fetch 函数的引用是因为我希望能够在回调和其他东西中使用它。

谢谢大家!

编辑:如果我在第二个 useEffect 中检查数据的真实性,它会起作用,但无论如何,这个实现能更好吗?

      useEffect(() => {
        fetchData(urlGoesHere);
      }, []);
    
      useEffect(() => {
        if (data) {
           setDisplayedItems(data);
           console.log(displayedItems);
        }
      }, [data]);

这是否意味着每次我想在加载时获取某些东西时都必须使用 2 个完整的 useEffects?

【问题讨论】:

  • 这是一个学习练习吗? “useFetch”钩子的其他实现已经存在(例如useSWR
  • @jsejcksn 差不多就是这样。我知道这不切实际,但尝试在我的第一个使用 React 的项目中保持原样,这样我就可以更多地理解 React。不过,我在某个时候查看了 SWR,所以感谢您提醒我。我会调查的

标签: javascript reactjs react-hooks


【解决方案1】:

除了来自钩子的数据之外,您还可以返回更多属性,以帮助您对渲染的内容和时间做出明智的选择。许多简单的useFetch 钩子(如您所询问的那个)中的一个常见模式是返回dataerrorisLoading,这样您就可以根据以下组合以声明方式呈现您想要的UI那些状态,在组件第一次渲染后仍然急切地获取数据。

如果您需要更好地控制何时获取数据(例如,您提到对“事件”的反应),只需在子组件中使用钩子,并根据您的“事件”有条件地渲染该子组件。下面是一个简单的 useFetch 钩子的典型示例(您可以在代码 sn-p 中看到它的工作原理):

这里是a link to just the hook in the TypeScript Playground,如果您不能或不想使用 TypeScript,可以使用它来访问和复制转译后的 JavaScript。

<div id="root"></div>
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.16.4/babel.min.js"></script><script>Babel.registerPreset('tsx', {presets: [[Babel.availablePresets['typescript'], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx,react">

/**
 * The following line is here because this Stack Overflow snippet uses the
 * UMD module for React. In your code, you'd use the commented `import` lines
 * below it.
 */
const {useEffect, useState} = React;

// import ReactDOM from 'react-dom';
// import {useEffect, useState} from 'react';
// import type {ReactElement} from 'react';

type FetchData<T> = {
  data: T | undefined;
  error: Error | undefined;
  isLoading: boolean;
};

function useFetch <T = unknown>(url: string): FetchData<T> {
  const [data, setData] = useState<T | undefined>();
  const [error, setError] = useState<Error | undefined>();
  const [isLoading, setIsLoading] = useState<boolean>(false);

  useEffect(() => {
    const ac = new AbortController();

    const fetchData = async () => {
      setIsLoading(true);
      try {
        const response = await fetch(url, {signal: ac.signal});
        if (!response.ok) throw new Error(String(response.status));
        const result = await response.json() as T;
        setData(result);
        setError(undefined);
      }
      catch (ex: unknown) {
        setError(ex instanceof Error ? ex : new Error(String(ex)));
      }
      setIsLoading(false);
    };

    fetchData();
    return () => ac.abort();
  }, [url, setData, setError, setIsLoading]);

  return {data, error, isLoading};
}

type User = { username: string };

function Example (): ReactElement {
  const url = 'https://jsonplaceholder.typicode.com/users';
  const {data, error, isLoading} = useFetch<User[]>(url);

  return (
    <div>
      <h1>Users</h1>
      {
        isLoading
          ? (<div>Loading users...</div>)
          : null
      }
      {
        error
          ? (<div>There was an error loading the data ({error.message})</div>)
          : null
      }
      {
        data
          ? (
            <ul>
              {data.map(({username}, index) => (
                <li key={`${index}-${username}`}>{username}</li>
              ))}
            </ul>
          )
          : null
      }
    </div>
  );
}

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

</script>

【讨论】:

  • 这回答了你的问题吗?
猜你喜欢
  • 1970-01-01
  • 2022-01-07
  • 2020-06-20
  • 2020-10-11
  • 1970-01-01
  • 2022-01-01
  • 2016-01-03
  • 2021-06-03
  • 1970-01-01
相关资源
最近更新 更多