You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13中PostContext内useEffect未执行导致数据为空的问题求助

问题分析与解决方案

核心问题:违反React Hook调用规则

你的代码中在useEffect的回调函数内部调用了usePostsData,如果这是一个自定义Hook(内部使用了其他React Hook,比如useState、useFetch等),这种写法直接违反了React的Hook规则——Hook只能在组件的顶层作用域或其他自定义Hook的顶层调用,不能在嵌套函数、条件判断或useEffect这类回调里调用。

这种违规会导致React抛出错误,直接阻止useEffect的执行,所以你看不到fetchPosts日志,数据也无法加载,listPost始终是空数组。

分情况修复

情况1:usePostsData是自定义Hook

如果usePostsData是用来获取数据的自定义Hook(比如基于useSWR、react-query或自己封装的Hook),需要将它移到组件顶层调用,再通过useEffect同步状态:

'use client'

import { createContext, useContext, useEffect, useState, ReactNode } from 'react';

const PostsContext = createContext<PostsContextType | undefined>(undefined);

export function PostsProvider({ children }: { children: ReactNode }) {
  const [posts, setPosts] = useState<Post[]>([]);
  const [listPost, setListPost] = useState<ListPost[]>([]);
  
  // 直接在组件顶层调用自定义Hook
  const { posts: fetchedPosts, listPost: fetchedListPost } = usePostsData();

  useEffect(() => {
    console.log("fetchPosts");
    // 当获取到数据时更新状态
    if (fetchedPosts.length > 0 && fetchedListPost.length > 0) {
      setPosts(fetchedPosts);
      setListPost(fetchedListPost);
    }
  }, [fetchedPosts, fetchedListPost]); // 添加依赖,确保数据更新时触发

  console.log("===============");

  return (
    <PostsContext.Provider value={{ posts, listPost }}>
      {children}
    </PostsContext.Provider>
  );
}

export function usePosts() {
  const context = useContext(PostsContext);
  if (!context) {
    throw new Error("usePosts must be used within a PostsProvider");
  }
  return {
    posts: context.posts,
    listPost: context.listPost,
  };
}

情况2:usePostsData是普通异步函数

如果usePostsData只是一个普通的异步数据请求函数(没有使用任何React Hook),请先重命名它(避免和Hook命名混淆,Hook必须以use开头),然后在useEffect中正常调用:

'use client'

import { createContext, useContext, useEffect, useState, ReactNode } from 'react';

const PostsContext = createContext<PostsContextType | undefined>(undefined);

// 重命名为普通异步函数,去掉use前缀
async function fetchPostsData() {
  // 示例:发起API请求
  const res = await fetch('/api/posts');
  if (!res.ok) throw new Error('Failed to fetch posts');
  return await res.json();
}

export function PostsProvider({ children }: { children: ReactNode }) {
  const [posts, setPosts] = useState<Post[]>([]);
  const [listPost, setListPost] = useState<ListPost[]>([]);

  useEffect(() => {
    console.log("fetchPosts");
    const fetchPosts = async () => {
      try {
        const { posts, listPost } = await fetchPostsData();
        setPosts(posts);
        setListPost(listPost);
      } catch (err) {
        console.error('Failed to load posts:', err); // 添加错误捕获
      }
    };

    fetchPosts();
  }, []);

  console.log("===============");

  return (
    <PostsContext.Provider value={{ posts, listPost }}>
      {children}
    </PostsContext.Provider>
  );
}

export function usePosts() {
  const context = useContext(PostsContext);
  if (!context) {
    throw new Error("usePosts must be used within a PostsProvider");
  }
  return {
    posts: context.posts,
    listPost: context.listPost,
  };
}

额外检查点

  1. Post页面是否为客户端组件:如果你的Post页面是服务器组件(没有加'use client'指令),无法调用usePosts这个客户端Hook。需要给Post页面添加'use client',或者将使用usePosts的部分抽离成单独的客户端组件,在服务器组件中引入。

示例(抽离客户端组件):

// app/post/[slug]/PostContent.tsx(客户端组件)
'use client'

export default function PostContent({ slug }: { slug: string }) {
  const { listPost } = usePosts();
  // 渲染内容逻辑
  return <div>{/* 基于listPost渲染 */}</div>;
}

// app/post/[slug]/page.tsx(服务器组件)
import PostContent from './PostContent';

export default function Post({ params }: { params: { slug: string } }) {
  return <PostContent slug={params.slug} />;
}
  1. 检查控制台错误:打开浏览器开发者工具的控制台,查看是否有React Hook相关的错误提示,这能帮你快速定位问题。

内容的提问来源于stack exchange,提问作者Ted Vu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 14:30:04