From 16f023fc666ad674d3dba89206230acbbf974735 Mon Sep 17 00:00:00 2001 From: Tyler Swavely Date: Fri, 23 Aug 2019 00:02:35 -0700 Subject: [PATCH 1/2] fix(useSetState): memoize setState callback --- src/useSetState.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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]; }; From c60ee96be1205281c9684d1ee3ec7ec122fdbccf Mon Sep 17 00:00:00 2001 From: Tyler Swavely Date: Fri, 23 Aug 2019 10:35:05 -0700 Subject: [PATCH 2/2] chore(useSetState): check useSetState for memoized callback --- src/__tests__/useSetState.test.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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); +});