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.
1 Answer
Reset to default 5To 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条)