diff --git a/src/hooks/useMergedState.ts b/src/hooks/useMergedState.ts index fc5a098b..44b609c1 100644 --- a/src/hooks/useMergedState.ts +++ b/src/hooks/useMergedState.ts @@ -5,7 +5,7 @@ import useState from './useState'; * Similar to `useState` but will use props value if provided. * Note that internal use rc-util `useState` hook. */ -export default function useControlledState( +export default function useMergedState( defaultStateValue: T | (() => T), option?: { defaultValue?: T | (() => T); @@ -49,16 +49,13 @@ export default function useControlledState( ); // Effect of reset value to `undefined` - const firstRenderRef = React.useRef(true); + const prevValueRef = React.useRef(value); React.useEffect(() => { - if (firstRenderRef.current) { - firstRenderRef.current = false; - return; - } - - if (value === undefined) { + if (value === undefined && value !== prevValueRef.current) { setInnerValue(value); } + + prevValueRef.current = value; }, [value]); return [mergedValue as unknown as R, triggerChange]; diff --git a/src/hooks/useState.ts b/src/hooks/useState.ts index 7cbfa8eb..ca4dded3 100644 --- a/src/hooks/useState.ts +++ b/src/hooks/useState.ts @@ -16,7 +16,7 @@ export type SetState = ( * We do not make this auto is to avoid real memory leak. * Developer should confirm it's safe to ignore themselves. */ -export default function useState( +export default function useSafeState( defaultValue?: T | (() => T), ): [T, SetState] { const destroyRef = React.useRef(false); diff --git a/tests/hooks.test.js b/tests/hooks.test.js index caa4f773..942684a6 100644 --- a/tests/hooks.test.js +++ b/tests/hooks.test.js @@ -76,6 +76,38 @@ describe('hooks', () => { const { container } = render(); expect(container.firstChild.textContent).toEqual('1'); }); + + it('React 18 should not reset to undefined', () => { + const Demo = () => { + const [val] = useMergedState(33, { value: undefined }); + + return
{val}
; + }; + + const { container } = render( + + + , + ); + + expect(container.querySelector('div').textContent).toEqual('33'); + }); + + it('postState', () => { + const Demo = () => { + const [val] = useMergedState(1, { postState: v => v * 2 }); + + return
{val}
; + }; + + const { container } = render( + + + , + ); + + expect(container.querySelector('div').textContent).toEqual('2'); + }); }); describe('useLayoutEffect', () => {