如何为使用 useQuery 获取的数据设置初始状态?

问题描述 投票:0回答:1

我正在使用 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
  • 第二个是博客数组(如屏幕截图所示)。

screenshot console log

看起来页面正在尝试渲染之前的初始状态(

undefined
),然后将状态设置为查询数据。是这样吗?我认为使用
useEffect
可以解决这个问题,但事实似乎并非如此。感谢任何帮助,谢谢。

javascript reactjs apollo
1个回答
5
投票

引用

useEffect
文档:

传递给

useEffect
的函数将在渲染提交到屏幕后运行。将效果视为从 React 的纯函数世界进入命令式世界的逃生舱口。

问题在于

useEffect
将在渲染后触发 after。这将导致以下一系列事件。假设
loading
刚刚设置为
false
并且获取数据时没有错误。

现在渲染组件时,

if (loading)
if (error)
保护子句将被跳过,因为数据已成功加载。然而
state
尚未更新,因为
useEffect
回调将在当前渲染后触发。此时当你拨打
renderBlogs(state)
state
仍会是
undefined
。这将引发您所描述的错误。

我可能缺少一些上下文,但从问题中显示的内容来看,没有理由使用

useEffect
。而是直接使用
data

Apollo 为

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),
});
© www.soinside.com 2019 - 2024. All rights reserved.