我正在使用 React 和 Apollo,我正在尝试使用 useQuery 钩子获取的数据来初始化状态,但在加载页面时出现“无法读取未定义的属性‘map’”。
const { loading, error, data } = useQuery(FETCH_BLOGS);
const [state, setState] = useState(undefined);
useEffect(() => {
if (loading === false && data) {
setState(data.blogs);
}
}, [loading, data]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error</p>;
在 JSX 中,我调用
{renderBlogs(state)}
,它映射数组,并且是抛出错误的地方。如果我传入初始数据{renderBlogs(data.blogs)}
它可以工作,但我需要将数据存储在状态中,因为它会发生变化。
当我
console.log(state)
时,它会记录两行:
undefined
。看起来页面正在尝试渲染之前的初始状态(
undefined
),然后将状态设置为查询数据。是这样吗?我认为使用 useEffect
可以解决这个问题,但事实似乎并非如此。感谢任何帮助,谢谢。
useEffect
文档:
传递给
的函数将在渲染提交到屏幕后运行。将效果视为从 React 的纯函数世界进入命令式世界的逃生舱口。useEffect
问题在于
useEffect
将在渲染后触发 after。这将导致以下一系列事件。假设 loading
刚刚设置为 false
并且获取数据时没有错误。
现在渲染组件时,
if (loading)
和 if (error)
保护子句将被跳过,因为数据已成功加载。然而 state
尚未更新,因为 useEffect
回调将在当前渲染后触发。此时当你拨打renderBlogs(state)
时state
仍会是undefined
。这将引发您所描述的错误。
我可能缺少一些上下文,但从问题中显示的内容来看,没有理由使用
useEffect
。而是直接使用 data
。
useQuery
提供的示例提供了一个很好的起点:
import { gql, useQuery } from '@apollo/client'; const GET_GREETING = gql` query GetGreeting($language: String!) { greeting(language: $language) { message } } `; function Hello() { const { loading, error, data } = useQuery(GET_GREETING, { variables: { language: 'english' }, }); if (loading) return <p>Loading ...</p>; return <h1>Hello {data.greeting.message}!</h1>; }
将该示例应用到您的场景中,它可能看起来非常相似。
const { loading, error, data } = useQuery(FETCH_BLOGS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error</p>;
return renderBlogs(data.blogs);
// or
return <Blogs blogs={data.blogs} />;
// depending on what helps you visualize the solution better
如果您需要设置器,您可能需要为问题提供更多背景信息。然而,拥有 setter 的一个常见原因可能是您想应用某种过滤器。一个简单的解决方案是计算博客状态。
const filteredBlogs = data.blogs.filter(blog => blog.title.includes(search));
这里
search
是某个 <input value={search} onChange={handleSearchChange} />
元素的状态。为了提高性能,您还可以选择在此处使用 useMemo
。
const filteredBlogs = useMemo(() => (
data.blogs.filter(blog => blog.title.includes(search))
), [data, search]);
如果您出于某种原因确实需要使用
useState
,您可以尝试使用 onCompleted
的 useQuery
选项来代替 useEffect
。
const [blogs, setBlogs] = useState();
const { loading, error } = useQuery(FETCH_BLOGS, {
onCompleted: data => setBlogs(data.blogs),
});