Last active
June 27, 2022 07:03
-
-
Save JakeCoxon/c7ebf6e6496f8468226fd36b596e1985 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useRef, useCallback, useEffect } from 'react'; | |
// A callback that always closes over the latest data but keeps the same | |
// identity and will not be called after component unmounts | |
const useStableCallback = callback => { | |
const callbackRef = useRef(); | |
const memoCallback = useCallback( | |
(...args) => callbackRef.current && callbackRef.current(...args), | |
[], | |
); | |
useEffect(() => { | |
callbackRef.current = callback; | |
return () => (callbackRef.current = undefined); | |
}); | |
return memoCallback; | |
}; | |
export default useStableCallback; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { renderHook } from 'react-hooks-testing-library'; | |
it('keeps identity', () => { | |
const useUberLinking = require('@zego/hooks/useStableCallback').default; | |
const hook = renderHook(callback => useUberLinking(callback), { | |
initialProps: x => x + 3, | |
}); | |
const initialIdentity = hook.result.current; | |
expect(hook.result.current).toEqual(expect.any(Function)); | |
expect(hook.result.current(4)).toBe(7); | |
hook.rerender(x => x + 10); | |
expect(hook.result.current(4)).toBe(14); | |
// Identity is still the same | |
expect(hook.result.current).toBe(initialIdentity); | |
}); | |
it('does not get called after unmounting', () => { | |
const useUberLinking = require('@zego/hooks/useStableCallback').default; | |
const mockFn = jest.fn(); | |
const hook = renderHook(callback => useUberLinking(callback), { | |
initialProps: mockFn, | |
}); | |
hook.unmount(); | |
hook.result.current(); | |
expect(mockFn).not.toHaveBeenCalled(); | |
}); |
Maybe there don't need useEffect
:
import { useRef, useCallback } from 'react';
function useEvent<Callback extends (...args: unknown[]) => unknown>(
callback: Callback,
) {
const callbackRef = useRef<Callback>();
callbackRef.current = callback;
const memoCallback = useCallback(
(...args: Parameters<Callback>) => callbackRef.current!(...args),
[],
);
return memoCallback;
}
export default useEvent;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thanks for sharing :)