替换useEffect为自定义usePropertyHistory致useReducer状态重置
自定义
usePropertyHistory钩子导致useReducer状态重置问题 我正在开发一个组件,需要在属性值从已定义变为未定义时执行特定逻辑。为此我实现了usePropertyHistory自定义钩子,它和原生useEffect的差异点:
- 仅接受单个依赖项
- 回调函数包含前后值参数
- 采用深度比较而非
Object.is进行依赖判断
但替换原生useEffect使用该钩子后,组件内的useReducer状态会被重置为初始值,而用原生useEffect时完全正常。
正常运行的代码(使用原生useEffect)
const MyComponent = ({ value }: ComponentProps) => { const initialState: DropDownBaseState = { isMultiSelect: allowMultiSelect, isListOpen: false, isPassedValue: false, }; const [state, dispatcher] = useReducer(reducer, initialState); useEffect(() => { if (value) { dispatcher({ type: DropDownBaseActionType.SetPassedValue, passedValue: value, }); } }, [value]); // 当上面的dispatcher调用后,状态变更会触发这个effect执行 useEffect(() => { if (state.selectedValue && state.selectedValue.length > 0 && textBoxRef.current) { textBoxRef.current.setInnerText(state.selectedValue[0].text); } else if (state.selectedValue && state.selectedValue?.length < 1) { setShowPlaceholder(true); } else if (!state.selectedValue) { setShowPlaceholder(true); } }, [state.selectedValue]); };
替换为自定义钩子后的问题代码
const MyComponent = ({ value }: ComponentProps) => { const initialState: DropDownBaseState = { isMultiSelect: allowMultiSelect, isListOpen: false, isPassedValue: false, }; const [state, dispatcher] = useReducer(reducer, initialState); usePropertyHistory((previousValue, currentValue) => { if (currentValue) { dispatcher({ type: DropDownBaseActionType.SetPassedValue, passedValue: currentValue, }); } else if (previousValue && !currentValue) { // 当前测试场景下这段逻辑从未执行 dispatcher({ type: DropDownBaseActionType.ClearSelectedValue, }); } }, value); // 上面的dispatcher调用后,这个effect从未触发... // 状态变更会在下一次渲染时发生,但紧接着的渲染中状态会被重置回初始值 useEffect(() => { if (state.selectedValue && state.selectedValue.length > 0 && textBoxRef.current) { textBoxRef.current.setInnerText(state.selectedValue[0].text); } else if (state.selectedValue && state.selectedValue?.length < 1) { setShowPlaceholder(true); } else if (!state.selectedValue) { setShowPlaceholder(true); } }, [state.selectedValue]); };
自定义usePropertyHistory钩子实现
const usePropertyHistory = <T>(callback: (previous: T | undefined, current: T | undefined) => void, property: T) => { const previousProperty = useRef<T>(); if (!deepEqual(previousProperty.current, property)) { callback(previousProperty.current, property); previousProperty.current = property; } }
已排查的测试结果
- 问题与React Strict Mode无关
- 即使仅调用
SetPassedValue动作,仍会出现状态重置,排除ClearSelectedValue逻辑的影响 - 用
useMemo包裹初始状态无法解决问题
想知道为什么原生useEffect能正常工作,但自定义钩子会导致状态重置?求各位提供解决思路。
内容的提问来源于stack exchange,提问作者Beehive Software Consultants
相关产品推荐
相关产品推荐

