React包装useState setter:避免timeframe变更触发两次API请求
解决timeframe变更时同步更新日期并避免重复API请求的方案
你的核心问题是避免timeframe变更时触发两次状态更新(进而两次API请求),最直接的解决思路是在更新timeframe的同时一次性计算并设置startDate和endDate,而不是依赖useEffect后续补更新。下面提供两种可行方案:
方案一:专门处理timeframe变更的回调函数
这种方式更直观,明确只处理时间范围的变更,避免状态更新的冗余:
1. 修正ParentComp的初始状态
首先给filters一个合法的初始值(原代码空对象不符合FilterType的类型约束):
interface FilterType { timeframe: string; startDate: Date; endDate: Date; } // 定义默认时间范围,比如"7d"代表最近7天 const DEFAULT_TIMEFRAME = "7d"; function ParentComp() { // 初始状态直接计算对应日期 const [filters, setFilters] = useState<FilterType>(() => { const { startDate, endDate } = getDatesFromTimeframe(DEFAULT_TIMEFRAME); return { timeframe: DEFAULT_TIMEFRAME, startDate, endDate }; }); // 包装timeframe变更逻辑:接收新的timeframe,一次性更新所有属性 const handleTimeframeChange = (newTimeframe: string) => { const { startDate, endDate } = getDatesFromTimeframe(newTimeframe); // 使用函数式更新确保拿到最新的prev状态 setFilters(prev => ({ ...prev, timeframe: newTimeframe, startDate, endDate })); }; return <ChildComp filters={filters} onTimeframeChange={handleTimeframeChange} />; }
2. 修改ChildComp的props和Dropdown绑定
function ChildComp({ filters, onTimeframeChange, }: { filters: FilterType; onTimeframeChange: (value: string) => void; }) { return <DropDown onChange={onTimeframeChange} value={filters.timeframe} />; }
这种方式只会触发一次状态更新,react-query自然只会发起一次API请求。
方案二:包装setFilters函数(保持原有props结构)
如果你不想修改ChildComp的props类型,希望继续传递setFilters,可以包装一个符合React.Dispatch<React.SetStateAction<FilterType>>类型的函数,自动处理timeframe变更时的日期同步:
function ParentComp() { const [filters, setFilters] = useState<FilterType>(() => { const { startDate, endDate } = getDatesFromTimeframe(DEFAULT_TIMEFRAME); return { timeframe: DEFAULT_TIMEFRAME, startDate, endDate }; }); // 包装setFilters,自动处理timeframe的日期同步 const wrappedSetFilters: React.Dispatch<React.SetStateAction<FilterType>> = (action) => { setFilters(prev => { // 先解析传入的action,得到要更新的部分状态 const update = typeof action === "function" ? action(prev) : action; // 如果更新内容包含timeframe,同步计算日期 if ("timeframe" in update) { const { startDate, endDate } = getDatesFromTimeframe(update.timeframe); return { ...prev, ...update, startDate, endDate }; } // 其他属性更新直接合并 return { ...prev, ...update }; }); }; return <ChildComp filters={filters} setFilters={wrappedSetFilters} />; }
这样ChildComp不需要做任何修改,调用setFilters更新timeframe时,会自动同步设置对应的日期,同样只会触发一次状态更新。
为什么之前的useEffect方案会触发两次请求?
因为你先通过Dropdown更新了filters.timeframe,这会触发一次状态变更和API请求;随后useEffect监听到timeframe变化,再次调用setFilters更新日期,又触发一次状态变更和请求。而上面的方案都是一次性完成所有必要的状态更新,从根源上避免了重复请求。
内容的提问来源于stack exchange,提问作者PepperoniPizza
相关产品推荐
相关产品推荐

