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

React Context数据传递异常:HomeScreen接收users为空问题排查

问题描述

我通过Supabase获取应用数据,并用React Context管理状态,核心代码如下:

export const DataContext = () => {
  const [posts, setPosts] = useState<Array<Post>>([]);
  const [profiles, setProfiles] = useState<Array<User>>([]);
  const [page, setPage] = useState<number>(1);
  const [perPage, setPerPage] = useState<number>(10);

  useEffect(() => {
    async function fetchData() {
      const { data: postsData } = await supabase
        .from("posts")
        .select("*")
        .range((page - 1) * perPage, page * perPage - 1);

      setPosts(postsData!.map((post) => new Post(post)));

      const postUids = postsData!.map((post) => post.uid);

      const { data: profilesData } = await supabase
        .from("profiles")
        .select("*")
        .in("uid", postUids);

      const profiles = profilesData!.map((userData: any) => {
        const userPosts = posts.filter((post) => post.uid === userData.uid);
        const user = new User({ ...userData, posts: userPosts });
        return user;
      });

      setProfiles((prevUsers) => [...prevUsers, ...profiles]);
    }

    fetchData();
  }, [page, perPage]);

  const nextPage = () => {
    setPage(page + 1);
  };

  const prevPage = () => {
    setPage(page - 1);
  };

  return React.createContext({ posts, profiles, nextPage, prevPage });
};

之后在TabBarView中使用Context实现页面数据共享:

const { posts, profiles } = useContext(DataContext());

const Home = (props: any) => (
  <View style={Theme().screen}>
    <HomeScreen posts={posts} users={users} />
  </View>
);

const Discover = (props: any) => (
  <View style={Theme().screen}>
    <ExploreScreen posts={posts} users={profiles} />
  </View>
);

最初的问题是Context中每个User的posts数组始终为空,我在TabBarView中重复编写了关联逻辑临时修复:

const { posts, profiles } = useContext(DataContext());

const users: Array<User> = profiles.map((user) => {                
  const userPosts = posts.filter((post) => post.uid === user.uid); 
  user.posts.push(...userPosts);                                   
  return user;                                                     
});                                                                

const Home = (props: any) => (
  <View style={Theme().screen}>
    <HomeScreen posts={posts} users={users} />
  </View>
);

const Discover = (props: any) => (
  <View style={Theme().screen}>
    <ExploreScreen posts={posts} users={profiles} />
  </View>
);

现在TabBarView中能正常打印posts和users,但传递给HomeScreen的users props为空,仅posts正常。

示例数据与日志

来自DataContext的示例数据:

Posts: [{
   "caption":"Caption",
   "date":"1669244422569",
   "imageUrls":[
      "https://cdn.pixabay.com/photo/2020/05/04/16/05/mckenzie-river-5129717__480.jpg"
   ],
   "location":{
      "latitude":150,
      "locationInfo":"City, State",
      "longitude":-150
   },
   "postId":"1669244407166",
   "uid":"daf6b8be-7cd0-4341-89d7-07879b207087"
}]

Users: [{
   "blockedUsers":[],
   "displayName":"name",
"photoURL":"https://cdn.pixabay.com/photo/2020/05/04/16/05/mckenzie-river-5129717__480.jpg",
   "uid":"daf6b8be-7cd0-4341-89d7-07879b207087",
   "verified":false
}]
  • TabBarView日志:
Posts: [Post, Post, Post]

Users: [User, User, User]
  • HomeScreen日志:
Posts: [Post, Post, Post]

Users: []

问题原因
  1. Context创建逻辑错误:原代码中每次调用DataContext组件都会创建新的Context实例,导致订阅组件无法稳定接收状态更新。
  2. useState异步更新导致数据关联失败:在Context的fetchData中,调用setPosts后立即用posts.filter关联用户与帖子,但useState是异步更新,此时posts还是初始空数组,导致生成的User实例posts为空。
  3. 组件定义位置引发闭包陷阱:在TabBarView内部定义Home、Discover组件,这些子组件的闭包会捕获初始的空users值,后续状态更新无法触发子组件重渲染。
  4. 直接修改原对象未触发重渲染:用user.posts.push直接修改User实例的属性属于突变操作,React无法检测到引用变化,不会触发HomeScreen的更新。

解决方案

1. 修复Context的创建与数据关联逻辑

将Context定义在组件外部,并用Provider统一提供状态,同时直接使用刚获取的原始数据关联用户与帖子:

// 外部定义Context,确保实例唯一
export const DataContext = React.createContext<{
  posts: Array<Post>;
  profiles: Array<User>;
  nextPage: () => void;
  prevPage: () => void;
}>({
  posts: [],
  profiles: [],
  nextPage: () => {},
  prevPage: () => {},
});

// 创建Provider组件封装状态逻辑
export const DataProvider = ({ children }: { children: React.ReactNode }) => {
  const [posts, setPosts] = useState<Array<Post>>([]);
  const [profiles, setProfiles] = useState<Array<User>>([]);
  const [page, setPage] = useState<number>(1);
  const [perPage, setPerPage] = useState<number>(10);

  useEffect(() => {
    async function fetchData() {
      const { data: postsData } = await supabase
        .from("posts")
        .select("*")
        .range((page - 1) * perPage, page * perPage - 1);

      const formattedPosts = postsData!.map((post) => new Post(post));
      setPosts(formattedPosts);

      const postUids = formattedPosts.map((post) => post.uid);

      const { data: profilesData } = await supabase
        .from("profiles")
        .select("*")
        .in("uid", postUids);

      const profiles = profilesData!.map((userData: any) => {
        // 直接用刚格式化的posts数据,避免异步状态更新的问题
        const userPosts = formattedPosts.filter(post => post.uid === userData.uid);
        return new User({ ...userData, posts: userPosts });
      });

      // 分页加载时去重,避免重复添加同一用户
      setProfiles(prev => {
        const existingUids = new Set(prev.map(u => u.uid));
        const newUsers = profiles.filter(u => !existingUids.has(u.uid));
        return [...prev, ...newUsers];
      });
    }

    fetchData();
  }, [page, perPage]);

  const nextPage = () => setPage(page + 1);
  const prevPage = () => setPage(page - 1);

  return (
    <DataContext.Provider value={{ posts, profiles, nextPage, prevPage }}>
      {children}
    </DataContext.Provider>
  );
};

2. 修复TabBarView的组件结构

将Home、Discover组件移到TabBarView外部,通过props传递数据,避免闭包陷阱;同时生成users时返回新的User实例,确保引用变化触发重渲染:

// 外部定义子组件,避免闭包问题
const Home = ({ posts, users }: { posts: Array<Post>, users: Array<User> }) => (
  <View style={Theme().screen}>
    <HomeScreen posts={posts} users={users} />
  </View>
);

const Discover = ({ posts, users }: { posts: Array<Post>, users: Array<User> }) => (
  <View style={Theme().screen}>
    <ExploreScreen posts={posts} users={users} />
  </View>
);

// TabBarView主组件
const TabBarView = () => {
  const { posts, profiles } = useContext(DataContext);

  // 生成关联后的users,返回新实例而非修改原对象
  const users = profiles.map(user => {
    const userPosts = posts.filter(post => post.uid === user.uid);
    return new User({ ...user, posts: userPosts });
  });

  return (
    <TabBar>
      <TabBar.Item label="Home">
        <Home posts={posts} users={users} />
      </TabBar.Item>
      <TabBar.Item label="Discover">
        <Discover posts={posts} users={profiles} />
      </TabBar.Item>
    </TabBar>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 16:20:38