javascript - How do I set initial state to data fetched with useQuery? - Stack Overflow

I'm working with React and Apollo and I'm trying to initialize state with data fetched using

I'm working with React and Apollo and I'm trying to initialize state with data fetched using useQuery hook but I'm getting "Cannot read property 'map' of undefined" when loading the page.

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>;

In the JSX I'm calling {renderBlogs(state)} which maps over the array and is where the error is being thrown. If I pass in the initial data {renderBlogs(data.blogs)} it works but I need to store the data in state as it will be mutated.

When I console.log(state) it logs 2 lines:

  • The first is undefined.
  • The second is the array of blogs (as shown in the screenshot).

It appears that the page is trying to render the initial state (undefined) before before the state is set to the query data. Is this the case? I thought using useEffect would solve this but that doesn't seem to be the case. Any help is appreciated, thank you.

I'm working with React and Apollo and I'm trying to initialize state with data fetched using useQuery hook but I'm getting "Cannot read property 'map' of undefined" when loading the page.

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>;

In the JSX I'm calling {renderBlogs(state)} which maps over the array and is where the error is being thrown. If I pass in the initial data {renderBlogs(data.blogs)} it works but I need to store the data in state as it will be mutated.

When I console.log(state) it logs 2 lines:

  • The first is undefined.
  • The second is the array of blogs (as shown in the screenshot).

It appears that the page is trying to render the initial state (undefined) before before the state is set to the query data. Is this the case? I thought using useEffect would solve this but that doesn't seem to be the case. Any help is appreciated, thank you.

Share Improve this question edited Dec 15, 2020 at 15:59 TheWolf1494 asked Dec 15, 2020 at 15:26 TheWolf1494TheWolf1494 961 silver badge6 bronze badges 0
Add a ment  | 

1 Answer 1

Reset to default 5

To quote the useEffect documentation:

The function passed to useEffect will run after the render is mitted to the screen. Think of effects as an escape hatch from React’s purely functional world into the imperative world.

The problem is that useEffect will trigger after a render. This will result in the following chain of events. Assume loading is just set to false and there where no errors fetching the data.

When the ponent is now being rendered both the if (loading) and if (error) guard clauses will be skipped, because the data successfully loaded. However state is not yet updated because the useEffect callback will trigger after the current render. At this point when you call renderBlogs(state) state will still be undefined. This will trow the error you describe.

I might be missing some context, but from what is shown in the question there is no reason to use useEffect. Instead use data directly.

The example provided by Apollo for useQuery provides a good starting point:

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>;
}

Applying the example to your scenario it might look very similar.

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

If you need the setter you might want to provide a bit more context to the question. However a mon reason to have a setter might be if you want to apply some sort of filter. A simple solution would be to have a puted blogs state.

const filteredBlogs = data.blogs.filter(blog => blog.title.includes(search));

Here search would be the state of some <input value={search} onChange={handleSearchChange} /> element. To improve performance you could also opt to use useMemo here.

const filteredBlogs = useMemo(() => (
  data.blogs.filter(blog => blog.title.includes(search))
), [data, search]);

If you really need to use useState for some reason, you could try the onCompleted option of useQuery instead of useEffect.

const [blogs, setBlogs] = useState();
const { loading, error } = useQuery(FETCH_BLOGS, {
  onCompleted: data => setBlogs(data.blogs),
});

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744400347a4572353.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信