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
相关产品推荐
相关产品推荐

