Home > database >  NextJS useEffect doesn't wait for API response
NextJS useEffect doesn't wait for API response

Time:11-03

I'm struggling to get "lovedList" values as the page loads for the first time. When re-rendering, the list is correctly updated. How can I make it so it waits for the list values to render the first objects from useEffect?

import { Post, PostProps } from "./Post";
import { useEffect, useState, useCallback } from "react";
import { api } from "../services/api";
import { useSession } from "next-auth/client";

export function PostList() {
  const [session, status] = useSession();
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState<PostProps[]>([])
  const [skip, setSkip] = useState(0)
  const take = 3

  let list = []

  async function getUserPurchases() {
    if (session) {
      const response = await api.get(`/purchases/${session?.id}`)

      response.data.map(purchase => {
        list.push(purchase.postId)
      })
    }

    return list
  }

  async function handleLoadMore() {
    setLoading(true)
    const lovedList = await getUserPurchases();
    setSkip(skip   take)
    const newPosts = await api.get(`/post?take=${take}&skip=${skip}`)

    const formattedData = newPosts.data.map(post => {
      console.log(lovedList)
      return {
        id: post.id,
        image: post.image,
        institution: post.institution,
        title: post.title,
        description: post.description,
        createdAt: new Date(post.createdAt).toLocaleDateString('pt-BR', {
          day: '2-digit',
          month: 'long',
          year: 'numeric'
        }),
        loved: lovedList.includes(post.id),
        author: {
          image: post.author.image,
          name: post.author.name,
          id: post.author.id
        }
      }
    })

    setData([...data, ...formattedData])
    setLoading(false)
  }

  useEffect(() => {
    handleLoadMore()
  }, [])

  if (loading) {
    return <h1>Carregando</h1>
  }
  return (
    <main className="flex flex-col md:max-w-3xl xl:max-w-6xl mx-auto text-gray-200 text-6xl ">
      {data && data.map(post => (
        <Post
          key={post.id}
          image={post.image}
          institution={post.institution}
          title={post.title}
          description={post.description}
          createdAt={post.createdAt}
          loved={post.loved}
          author={post.author}
          id={post.id}
        />
      ))}

      <button
        type="button"
        className="px-4 py-3 bg-blue-400 rounded-full outline-none flex items-center 
        justify-center text-sm my-16 mx-auto w-4/12 hover:bg-blue-500
        transition-colors duration-200 ease-out"
        onClick={handleLoadMore}
      >
        Carregar Mais
      </button>
    </main>
  );
}
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

When I load the page, the console.log returns empty arrays. If the next few items load, they console.log the propper list. How can I make it so it load properly when accessing the page for the first time?

Thanks!

CodePudding user response:

It doest wait because even though handleLoadMore is an async function, inside the useEffect there is no awaityng, the function just go to the end and loading changes from true to false almost instantly.

useEffect(() => {
    // setLoading(true) - This setLoading is not necessary since true is the default.
    handleLoadMore().then(() => {
      setLoading(false)
    })   
}, []) // This only runs once, so the empty Array.

CodePudding user response:

You can make use either one of these:-

A. use your loading state
  • only display data if loading=false
  return (
    <main className="flex flex-col md:max-w-3xl xl:max-w-6xl mx-auto text-gray-200 text-6xl ">
      {!loading 
        ? data.map(post => (
          <Post
            key={post.id}
            image={post.image}
            institution={post.institution}
            title={post.title}
            description={post.description}
            createdAt={post.createdAt}
            loved={post.loved}
            author={post.author}
            id={post.id}
          />
        ))
        : 'Loading...'
      }

      <button
        type="button"
        className="px-4 py-3 bg-blue-400 rounded-full outline-none flex items-center 
        justify-center text-sm my-16 mx-auto w-4/12 hover:bg-blue-500
        transition-colors duration-200 ease-out"
        onClick={handleLoadMore}
      >
        Carregar Mais
      </button>
    </main>
  );
B. set your data state initially to undefined
  • currently you set them as const [data, setData] = useState<PostProps[]>([])
  • change it to undefined, const [data, setData] = useState<PostProps[]>(). I'm not sure whether you can do it like this or not. I've never use this useState<Props>([]) syntax before
  • then your current condition data && data.map(post => (... will work as intended.
  • It didn't work before cause data was set to be []/empty array initially.
  • Related