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

如何给传给子组件的setState添加重载并保留获取prevState的能力?

解决封装setState时兼容函数式更新的问题

你的核心问题是封装的$setIsOpen没有兼容React原生更新函数支持的函数式参数。原生useState返回的更新函数可以接受两种输入:直接的新状态值,或者接收上一状态并返回新状态的函数。要实现完全兼容,需要在封装函数中区分参数类型并做对应处理。

JavaScript 实现

import { useState } from 'react';

function Child({ isOpen, setIsOpen }) {
  return (
    <div>
      <button onClick={() => setIsOpen(true)}>设为开启</button>
      <button onClick={() => setIsOpen(prev => !prev)}>切换状态</button>
      <p>当前状态:{isOpen ? '已开启' : '已关闭'}</p>
    </div>
  );
}

function Parent() {
  const [isOpen, setIsOpen] = useState(false);

  const $setIsOpen = (update) => {
    // 执行你的额外操作,比如日志、埋点等
    console.log("触发状态更新,执行额外操作");

    // 区分参数是值还是函数式更新
    if (typeof update === 'function') {
      // 传入函数时,委托给原生setIsOpen,同时可在内部基于新状态做操作
      setIsOpen(prevState => {
        const newState = update(prevState);
        console.log(`状态更新完成:${prevState} → ${newState}`);
        return newState;
      });
    } else {
      // 传入值时直接传递,并执行额外操作
      console.log(`状态更新完成:${isOpen} → ${update}`);
      setIsOpen(update);
    }
  };

  return <Child isOpen={isOpen} setIsOpen={$setIsOpen} />;
}

export default Parent;

TypeScript 实现

TypeScript中可以直接复用React内置的Dispatch<SetStateAction<T>>类型,确保封装函数的类型和原生更新函数完全一致:

import { useState, Dispatch, SetStateAction } from 'react';

// 定义子组件Props类型
type ChildProps = {
  isOpen: boolean;
  setIsOpen: Dispatch<SetStateAction<boolean>>;
};

function Child({ isOpen, setIsOpen }: ChildProps) {
  return (
    <div>
      <button onClick={() => setIsOpen(true)}>设为开启</button>
      <button onClick={() => setIsOpen(prev => !prev)}>切换状态</button>
      <p>当前状态:{isOpen ? '已开启' : '已关闭'}</p>
    </div>
  );
}

function Parent() {
  const [isOpen, setIsOpen] = useState(false);

  // 直接使用React内置类型,保证类型兼容
  const $setIsOpen: Dispatch<SetStateAction<boolean>> = (update) => {
    // 执行额外操作
    console.log("触发状态更新,执行额外操作");

    if (typeof update === 'function') {
      setIsOpen(prevState => {
        const newState = update(prevState);
        console.log(`状态更新完成:${prevState} → ${newState}`);
        return newState;
      });
    } else {
      console.log(`状态更新完成:${isOpen} → ${update}`);
      setIsOpen(update);
    }
  };

  return <Child isOpen={isOpen} setIsOpen={$setIsOpen} />;
}

export default Parent;

关键说明

  • 当传入函数式更新参数时,必须把这个函数传递给原生setIsOpen,由React在合适时机调用它并传入最新的prevState,不能自己提前执行(否则会拿到过时的状态)。
  • 如果你的额外操作不需要依赖新状态,可以简化代码,直接把update参数传递给原生setIsOpen,不需要区分类型:
    const $setIsOpen = (update) => {
      console.log("执行额外操作");
      setIsOpen(update);
    };
    
    这种写法也能兼容两种更新方式,只是无法在额外操作中获取新状态值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 07:25:24