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

React类组件转函数组件时useEffect获取prevProps问题求助

解决React函数组件重构中useEffect获取prevProps的问题

我来帮你梳理下问题所在,以及对应的解决方案:

问题诊断

你的代码里有几个关键问题导致了prevItems显示undefined,且无法像类组件那样监听items变化重置页码:

  1. 自定义usePrevious Hook写法错误
    你内部的useEffect没有指定依赖[value],导致ref不会在props.items变化时更新;同时把这个Hook定义在组件内部,虽然能运行,但不符合复用性原则,且容易引发不必要的重新创建。

  2. useEffect依赖数组设置错误
    第二个useEffect的依赖是空数组,只会在组件挂载时执行一次,后续props.items变化时不会触发,完全失去了原componentDidUpdate的监听能力。

  3. useState初始化结构错误
    useState({ pager: {} })会让pager的结构变成{ pager: {} },和原类组件的state.pager(直接是分页对象)结构不一致,会导致后续分页逻辑出错。

修正后的完整代码

首先,先定义通用的usePrevious Hook(建议放在组件外部,方便复用):

const usePrevious = (value) => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  }, [value]); // 仅当value变化时更新ref
  return ref.current;
};

然后是重构后的函数组件:

function Pagination2(props) {
  // 修正useState初始化,和原类组件state结构保持一致
  const [pager, setPager] = useState({});
  const prevItems = usePrevious(props.items);

  // 实现原类组件的setPage方法
  const setPage = (page) => {
    const { items, pageSize, onChangePage } = props;
    if (!items || items.length === 0) return;

    const totalItems = items.length;
    let currentPage = page;
    const totalPages = Math.ceil(totalItems / pageSize);

    // 边界值处理
    if (currentPage < 1) currentPage = 1;
    else if (currentPage > totalPages) currentPage = totalPages;

    // 计算分页参数
    const startIndex = (currentPage - 1) * pageSize;
    const endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
    const pages = [...Array(totalPages).keys()].map(i => i + 1);

    // 更新分页状态
    setPager({
      totalItems,
      currentPage,
      totalPages,
      startIndex,
      endIndex,
      pages
    });

    // 触发页码变化回调
    onChangePage(currentPage);
  };

  // 对应componentDidMount:初始化分页
  useEffect(() => {
    if (props.items && props.items.length) {
      setPage(props.initialPage);
    }
  }, [props.items, props.initialPage]); // 添加依赖,确保初始值变化时能更新

  // 对应componentDidUpdate:监听items变化,重置到初始页
  useEffect(() => {
    // 排除第一次渲染时prevItems为undefined的情况
    if (prevItems !== undefined && props.items !== prevItems) {
      setPage(props.initialPage);
    }
  }, [props.items, prevItems, props.initialPage]); // 添加必要依赖

  // 这里返回分页组件的JSX(根据你的需求自定义)
  return (
    <div className="pagination">
      {/* 示例:上一页按钮 */}
      <button onClick={() => setPage(pager.currentPage - 1)} disabled={pager.currentPage === 1}>
        上一页
      </button>
      {/* 页码按钮列表 */}
      {pager.pages?.map(pageNum => (
        <button
          key={pageNum}
          onClick={() => setPage(pageNum)}
          style={{ backgroundColor: pager.currentPage === pageNum ? '#ccc' : 'transparent' }}
        >
          {pageNum}
        </button>
      ))}
      {/* 下一页按钮 */}
      <button onClick={() => setPage(pager.currentPage + 1)} disabled={pager.currentPage === pager.totalPages}>
        下一页
      </button>
    </div>
  );
}

// 保留原有的propTypes和defaultProps
Pagination2.propTypes = {
  items: PropTypes.array.isRequired,
  onChangePage: PropTypes.func.isRequired,
  initialPage: PropTypes.number,
  pageSize: PropTypes.number,
};

Pagination2.defaultProps = {
  initialPage: 1,
  pageSize: 5,
};

关键说明

  • usePrevious的正确用法:通过useRef保存上一轮渲染的props值,仅当目标值变化时更新ref,确保拿到的是准确的前一次值。
  • useEffect依赖数组:必须添加相关的props作为依赖,这样React才能在这些值变化时重新执行副作用函数,实现类组件componentDidUpdate的逻辑。
  • 第一次渲染的特殊处理:因为第一次渲染时prevItems是undefined,所以需要在判断中排除这个情况,避免不必要的页码重置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 16:33:14