diff --git a/src/__tests__/useSetState.test.ts b/src/__tests__/useSetState.test.ts index 684ba30bee..4f3b1e39bb 100644 --- a/src/__tests__/useSetState.test.ts +++ b/src/__tests__/useSetState.test.ts @@ -40,3 +40,21 @@ it('should merge changes into current state when providing function', () => { expect(result.current[0]).toEqual({ foo: 'bar', count: 2, someBool: true }); }); + +/** + * Enforces cases where a hook can safely depend on the callback without + * causing an endless rerender cycle: useEffect(() => setState({ data }), [setState]); + */ +it('should return a memoized setState callback', () => { + const { result, rerender } = setUp({ ok: false }); + const [, setState1] = result.current; + + act(() => { + setState1({ ok: true }); + }); + rerender(); + + const [, setState2] = result.current; + + expect(setState1).toBe(setState2); +}); diff --git a/src/useSetState.ts b/src/useSetState.ts index d09004390e..ba40da2139 100644 --- a/src/useSetState.ts +++ b/src/useSetState.ts @@ -1,12 +1,15 @@ -import { useState } from 'react'; +import { useState, useCallback } from 'react'; const useSetState = ( initialState: T = {} as T ): [T, (patch: Partial | ((prevState: T) => Partial)) => void] => { const [state, set] = useState(initialState); - const setState = patch => { - set(prevState => Object.assign({}, prevState, patch instanceof Function ? patch(prevState) : patch)); - }; + const setState = useCallback( + patch => { + set(prevState => Object.assign({}, prevState, patch instanceof Function ? patch(prevState) : patch)); + }, + [set] + ); return [state, setState]; };