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

React useEffect钩子结合Supabase实时功能的分页数据异常问题

问题原因与解决办法

问题根源

你遇到的问题是典型的闭包捕获过时状态导致的:

  • 你的 useEffect 依赖数组是空的,只会在组件第一次挂载时执行。此时它创建的 Supabase 订阅,其回调函数 fetchData 引用的是初始的 page 状态(值为 0)。
  • 当你用分页器切换页码时,page 状态更新了,但挂载时创建的订阅仍然拿着旧的 fetchData 函数——这个旧函数根本不知道 page 已经变了,所以订阅触发时还是会用 page=0 去请求数据。

解决办法一:用 Ref 跟踪最新页码

通过 useRef 保存当前的页码值,让 fetchData 每次都读取最新的页码,绕过闭包的限制:

export default function Index() {
    const [users, setUsers] = useState([]);
    const [page, setPage] = useState(0);
    // 用 Ref 存最新的页码
    const pageRef = useRef(page);

    // 页码更新时同步 Ref 的值
    useEffect(() => {
        pageRef.current = page;
    }, [page]);

    const fetchData = async () => {
        // 读取 Ref 里的最新页码
        const currentPage = pageRef.current;
        const { data } = await supabaseClient
          .from<User>('users')
          .select('id, email')
          .range(currentPage * 10, currentPage * 10 + 9);

        setUsers(data);
    };

    useEffect(() => {
        fetchData();

        const usersSubscription = supabaseClient
          .from<User>('users')
          .on('*', fetchData)
          .subscribe();

        const removeSubscription = async () => {
          await supabaseClient.removeSubscription(usersSubscription);
        };

        return removeSubscription;
    }, []);

    return (
        <>
            <UserList users={users}/>
            <Paginator page={page} onPageChange={setPage} />
        </>
    );
}

这种方法不需要频繁销毁重建订阅,性能更好,适合经常切换页码的场景。

解决办法二:更新 useEffect 依赖,重建订阅

用 useCallback 缓存 fetchData,并把它加到 useEffect 的依赖数组里,这样页码变化时会自动重建订阅,确保回调用的是最新的 fetchData:

export default function Index() {
    const [users, setUsers] = useState([]);
    const [page, setPage] = useState(0);

    // 用 useCallback 缓存 fetchData,只有 page 变了才更新
    const fetchData = useCallback(async () => {
        const { data } = await supabaseClient
          .from<User>('users')
          .select('id, email')
          .range(page * 10, page * 10 + 9);

        setUsers(data);
    }, [page]);

    useEffect(() => {
        fetchData();

        const usersSubscription = supabaseClient
          .from<User>('users')
          .on('*', fetchData)
          .subscribe();

        const removeSubscription = async () => {
          await supabaseClient.removeSubscription(usersSubscription);
        };

        return removeSubscription;
    }, [fetchData]); // 依赖 fetchData,页码变了就重新执行

    return (
        <>
            <UserList users={users}/>
            <Paginator page={page} onPageChange={setPage} />
        </>
    );
}

这种写法更符合 React 的钩子依赖规则,逻辑更直观,但每次切换页码都会销毁旧订阅并创建新的,适合页码切换不频繁的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 22:20:26