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

React自定义Hook闭包问题:curPage无法正确递增

React自定义Hook闭包问题解析与修复

问题原因

你遇到的核心问题是React组件渲染机制与闭包结合导致变量无法持久化:

  • 组件每次重新渲染时,usePagination Hook会被重新执行,函数内部的let curPage = 1;会被重新初始化,每次渲染都会得到一个全新的curPage变量。
  • getItems是Hook每次执行时创建的新函数,它捕获的是当前渲染周期内的curPage。调用getItems时,虽然会把当前的curPage从1加到2,但这个修改只存在于当前渲染的变量副本中。
  • 一旦setItemsToRender触发组件重新渲染,usePagination再次执行,curPage又被重置为1,导致下次调用getItems时仍从1开始累加,永远停在2。

把curPage移到全局能正常工作,是因为全局变量不会随组件渲染重置,但这种写法会导致多个组件共用同一个curPage,完全破坏了自定义Hook的复用性,绝对不推荐。

正确修复方案

要在Hook中持久化跨渲染的变量,应该使用React提供的useState或useRef,根据需求选择:

方案1:用useState管理curPage(推荐,适合需要同步视图更新的场景)

import { useState } from 'react';

const usePagination = items => {
  const [itemsToRender, setItemsToRender] = useState(items.slice(0, 4));
  // 用useState持久化curPage,初始值设为1
  const [curPage, setCurPage] = useState(1);
  const maximumItems = 4;
  const totalPages = Math.ceil(items.length / maximumItems);

  const getItems = () => {
    const nextPage = curPage + 1;
    // 增加页数判断,避免超过总页数后无效调用
    if (nextPage > totalPages) return;
    
    const min = (nextPage - 1) * maximumItems;
    const max = nextPage * maximumItems;

    setItemsToRender(state => state.concat(items.slice(min, max)));
    setCurPage(nextPage);
  };

  return { getItems, itemsToRender, curPage, totalPages };
};

export default usePagination;
  • useState保存的curPage会在组件整个生命周期内保持状态,每次更新通过setCurPage触发,确保跨渲染周期的一致性。
  • 新增的页数判断能防止超出总页数后继续调用导致的逻辑错误。

方案2:用useRef管理curPage(适合curPage变化不需要触发视图更新的场景)

如果组件不需要根据curPage的变化重新渲染,仅在内部逻辑中使用,可以用useRef:

import { useState, useRef } from 'react';

const usePagination = items => {
  const [itemsToRender, setItemsToRender] = useState(items.slice(0, 4));
  // 用useRef持久化curPage,.current属性保存实际值
  const curPageRef = useRef(1);
  const maximumItems = 4;
  const totalPages = Math.ceil(items.length / maximumItems);

  const getItems = () => {
    curPageRef.current += 1;
    if (curPageRef.current > totalPages) {
      curPageRef.current = totalPages;
      return;
    }
    
    const min = (curPageRef.current - 1) * maximumItems;
    const max = curPageRef.current * maximumItems;

    setItemsToRender(state => state.concat(items.slice(min, max)));
  };

  // 对外暴露curPage时,返回curPageRef.current
  return { getItems, itemsToRender, curPage: curPageRef.current, totalPages };
};

export default usePagination;
  • useRef的.current属性会在组件整个生命周期中保持不变,不会随渲染重置,且修改它不会触发组件重新渲染。

关键知识点总结

  • React组件每次渲染时,自定义Hook的函数体都会重新执行,内部普通变量(let/const声明)会被重新初始化,无法跨渲染持久化。
  • 只有React提供的状态管理Hook(useState/useRef/useReducer等)能在组件多次渲染间保存值。
  • 闭包捕获的是变量的引用,但如果变量在每次渲染时都被重新创建,闭包捕获的就是每次的新变量,自然无法累积更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 00:29:53