Reputation: 69
I have a useEffect with fetch and I would like to render it first, since it runs the else case first and then runs the useEffect. Which shows the else case then the posts when viewing the page. Is there a way to render useEffect first?
const [posts, setPosts] = useState([]);
useEffect(() => {
const data = await fetch('https://localhost:1111/api/posts', {
credentials: 'include'
});
const response = await data.json();
setPosts(response);
}, []);
return (
<div>
{posts.length > 0 ? (
posts.map(post => (
<Post key={post.id} onUpdate={() => handleUpdate()} post={post} />
))
) : (
<p>No posts</p>
)}
</div>
);
Upvotes: 3
Views: 13959
Reputation: 84982
You can get that result, but you'll need to do it a different way.
When your component renders for the first time, the entire function will run, and then the page will be updated based on what your function returned. This happens more or less synchronously. Only after it has rendered will your effects run. It's not possible to change the order of this: render is always first, effects are always after the render.
So if you don't want anything to display until that effect can complete, you need to return a null from the render. Then when the fetch is complete, you update state, which causes it to render again, this time not with a null.
For example, here i've taken your code and added one more possibility: the state might be null. I'm using this to indicate that it hasn't loaded yet. Later, once the data has been fetched, the state gets updated and the component rerenders.
const [posts, setPosts] = useState(null);
useEffect(() => {
(async () => {
const data = await fetch('https://localhost:1111/api/posts', {
credentials: 'include'
});
const response = await data.json();
setPosts(response);
})();
}, []);
if (posts === null) {
return null;
}
return (
<div>
{posts.length > 0 ? (
posts.map(post => (
<Post key={post.id} onUpdate={() => handleUpdate()} post={post} />
))
) : (
<p>No posts</p>
)}
</div>
);
Upvotes: 4
Reputation: 4068
You use useEffect
as componentDidMount
because you provide an empty array as dependency. As its name suggests it's call after your component first render.
So to answer to your question, no you can't render your fetch result as first render. You can use a Loader
component to improve UX and wait until your data get fetched.
Or you can fetch your data in a parent component and pass it down as prop so you can first render with your fetched data.
Upvotes: 0
Reputation: 57192
You could set posts to null
by default, rather than an empty array. Then render nothing (or a spinner) if posts
is null
. This would prevent flashing "no posts" while the data fetching is occurring.
Upvotes: 0