Skip to content

Commit

Permalink
fix(useMap): methods with side effects should be stable across renders.
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan Loeschcke committed Dec 9, 2019
1 parent dbee45c commit 020b4db
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 6 deletions.
19 changes: 13 additions & 6 deletions src/useMap.ts
@@ -1,18 +1,20 @@
import { useState, useMemo } from 'react';
import { useState, useMemo, useCallback } from 'react';

export interface Actions<T extends object> {
get: <K extends keyof T>(key: K) => T[K];
export interface StableActions<T extends object> {
set: <K extends keyof T>(key: K, value: T[K]) => void;
remove: <K extends keyof T>(key: K) => void;
reset: () => void;
}

export interface Actions<T extends object> extends StableActions<T> {
get: <K extends keyof T>(key: K) => T[K];
}

const useMap = <T extends object = any>(initialMap: T = {} as T): [T, Actions<T>] => {
const [map, set] = useState<T>(initialMap);

const utils = useMemo<Actions<T>>(
const stableActions = useMemo<StableActions<T>>(
() => ({
get: key => map[key],
set: (key, entry) => {
set(prevMap => ({
...prevMap,
Expand All @@ -27,9 +29,14 @@ const useMap = <T extends object = any>(initialMap: T = {} as T): [T, Actions<T>
},
reset: () => set(initialMap),
}),
[map, set]
[set]
);

const utils = {
get: useCallback(key => map[key], [map]),
...stableActions,
} as Actions<T>;

return [map, utils];
};

Expand Down
14 changes: 14 additions & 0 deletions tests/useMap.test.ts
Expand Up @@ -121,3 +121,17 @@ it('should reset map to initial object provided', () => {

expect(result.current[0]).toEqual({ foo: 'bar', a: 1 });
});

it('should memoize actions with side effects', () => {
const { result } = setUp({ foo: 'bar', a: 1 });
const [, utils] = result.current;
const { set, remove, reset } = utils;

act(() => {
set('foo', 'baz');
});

expect(result.current[1].set).toBe(set);
expect(result.current[1].remove).toBe(remove);
expect(result.current[1].reset).toBe(reset);
});

0 comments on commit 020b4db

Please sign in to comment.