使用自定义钩子获取后尝试访问数据不起作用
Posted
技术标签:
【中文标题】使用自定义钩子获取后尝试访问数据不起作用【英文标题】: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,所以感谢您提醒我。我会调查的
【参考方案1】:
除了来自钩子的数据之外,您还可以返回更多属性,以帮助您对渲染的内容和时间做出明智的选择。许多简单的useFetch
钩子(如您所询问的那个)中的一个常见模式是返回data
、error
和isLoading
,这样您就可以根据以下组合以声明方式呈现您想要的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>
【讨论】:
这回答了你的问题吗?以上是关于使用自定义钩子获取后尝试访问数据不起作用的主要内容,如果未能解决你的问题,请参考以下文章
尽管控制台没有显示错误,但 useHistory 钩子不起作用
使用 jest 模拟 react-router-dom 钩子不起作用