NextJS中useEffect未等待API响应 首次加载lovedList为空如何解决
问题原因
- 核心是
useSession的加载时序问题:next-auth的useSession在组件首次挂载时还未完成鉴权校验,此时session值为null,首次执行getUserPurchases时直接跳过了接口请求逻辑,返回空数组。 - 原useEffect只在组件首次挂载时触发一次,没有监听session的加载状态,等到session加载完成后不会重新触发数据拉取逻辑。
- 组件外部声明的
list全局变量存在闭包风险,多次调用可能累积脏数据。
修复方案
- 新增判断:只有session加载完成(
status !== 'loading')之后才执行数据拉取逻辑 - 调整useEffect依赖数组,加入session和status作为依赖,保证session加载完成后自动触发首次数据拉取
- 移除全局的
list变量,直接在getUserPurchases内部处理并返回结果,避免闭包问题 - 用useCallback包裹handleLoadMore,补全依赖,避免重复渲染导致的函数重复创建
- 状态更新改用函数式写法,避免闭包拿到旧的状态值
修改后代码
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 async function getUserPurchases() { if (!session) return [] const response = await api.get(`/purchases/${session.id}`) // 直接返回处理后的数组,不用外部变量存储 return response.data.map(purchase => purchase.postId) } // 用useCallback包裹,补全所有依赖 const handleLoadMore = useCallback(async () => { // session还在加载时直接返回,不执行后续逻辑 if (status === 'loading') return setLoading(true) const lovedList = await getUserPurchases(); 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(prev => [...prev, ...formattedData]) setSkip(prev => prev + take) setLoading(false) }, [session, status, skip, take]) // 调整useEffect依赖,session加载完成且无数据时自动触发首次加载 useEffect(() => { if (status !== 'loading' && data.length === 0) { handleLoadMore() } }, [status, session, data.length, 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> ); }
内容的提问来源于stack exchange,提问作者Vitor
相关产品推荐
相关产品推荐

