From 2a62f1783a53134aec0e62e76f15b3adbabd8ef3 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 30 Sep 2023 15:02:13 +0200 Subject: [PATCH 01/12] feat: add useShallow See - https://github.com/pmndrs/zustand/discussions/1937 - https://github.com/pmndrs/zustand/discussions/1937#discussioncomment-7118242 - https://github.com/pmndrs/zustand/discussions/1937#discussioncomment-6974554 --- src/shallow.ts | 16 ++++++ tests/shallow.test.tsx | 124 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/src/shallow.ts b/src/shallow.ts index 0cce793019..c8acd6d26a 100644 --- a/src/shallow.ts +++ b/src/shallow.ts @@ -1,3 +1,5 @@ +import { useMemo, useRef } from 'react' + export function shallow(objA: T, objB: T) { if (Object.is(objA, objB)) { return true @@ -59,3 +61,17 @@ export default ((objA, objB) => { } return shallow(objA, objB) }) as typeof shallow + +export function useShallow(selector: (state: S) => U): (state: S) => U { + const prev = useRef() + + return useMemo( + () => (state) => { + const next = selector(state) + return shallow(prev.current, next) + ? (prev.current as U) + : (prev.current = next) + }, + [selector] + ) +} diff --git a/tests/shallow.test.tsx b/tests/shallow.test.tsx index ddc176520c..17d7240620 100644 --- a/tests/shallow.test.tsx +++ b/tests/shallow.test.tsx @@ -1,6 +1,9 @@ +import { useState } from 'react' +import { act, fireEvent, render, renderHook } from '@testing-library/react' import { describe, expect, it } from 'vitest' import { create } from 'zustand' -import { shallow } from 'zustand/shallow' +import { useStore } from 'zustand/react' +import { shallow, useShallow } from 'zustand/shallow' describe('shallow', () => { it('compares primitive values', () => { @@ -131,3 +134,122 @@ describe('unsupported cases', () => { ).not.toBe(false) }) }) + +describe('useShallow', () => { + const useTestShallowSimple = (props: { + selector: (state: S) => U + state: S + }): { selectorOutput: U; useShallowOutput: U } => { + const useShallowOutput = useShallow(props.selector)(props.state) + return { + selectorOutput: props.selector(props.state), + useShallowOutput, + } + } + + it('input and output selectors always return shallow equal values', () => { + const initialProps = { + selector: (state: Record) => Object.keys(state), + state: { a: 1, b: 2 } as Record, + } + + const res = renderHook((props) => useTestShallowSimple(props), { + initialProps, + }) + + expect(res.result.current.selectorOutput).toEqual( + res.result.current.useShallowOutput + ) + + res.rerender({ + state: { a: 1, b: 2, c: 3 }, + selector: initialProps.selector, + }) + }) + + it('returns the previously computed instance when possible', () => { + const initialProps = { + selector: Object.keys, + state: { a: 1, b: 2 } as Record, + } + + const res = renderHook((props) => useTestShallowSimple(props), { + initialProps, + }) + + const output1 = res.result.current.useShallowOutput + + res.rerender({ + state: initialProps.state, + selector: (state: Record) => Object.keys(state), // New selector instance + }) + const output2 = res.result.current.useShallowOutput + + expect(output1).toBe(output2) + res.rerender(initialProps) + + expect(res.result.current.useShallowOutput).toBe(output1) + }) + + it('only re-renders if selector output has changed according to shallow', () => { + let countRenders = 0 + const store = create((): Record => ({ a: 1, b: 2, c: 3 })) + const TestShallow = ({ + selector = (state) => Object.keys(state).sort(), + }: { + selector?: (state: Record) => string[] + }) => { + const output = useStore(store, useShallow(selector)) + + ++countRenders + + return
{output.join(',')}
+ } + + expect(countRenders).toBe(0) + const res = render() + + expect(countRenders).toBe(1) + expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c') + + act(() => { + store.setState({ a: 4 }) // This will not cause a re-render. + }) + + expect(countRenders).toBe(1) + + act(() => { + store.setState({ d: 10 }) // This will cause a re-render. + }) + + expect(countRenders).toBe(2) + expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c,d') + }) + + it('does not cause state closure issues', () => { + const store = create((): Record => ({ a: 1, b: 2, c: 3 })) + const TestShallowWithState = () => { + const [count, setCount] = useState(0) + const output = useStore( + store, + useShallow((state) => Object.keys(state).concat([count.toString()])) + ) + + return ( +
setCount((prev) => ++prev)}> + {output.join(',')} +
+ ) + } + + const res = render() + + expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c,0') + + fireEvent.click(res.getByTestId('test-shallow')) + + expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c,1') + }) +}) From 51ef6fcbb6637a255be91778b84d5743d9133be5 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 30 Sep 2023 15:27:40 +0200 Subject: [PATCH 02/12] chore(useShallow): improve unit tests --- tests/shallow.test.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/shallow.test.tsx b/tests/shallow.test.tsx index 17d7240620..ce0f8c71e4 100644 --- a/tests/shallow.test.tsx +++ b/tests/shallow.test.tsx @@ -165,6 +165,10 @@ describe('useShallow', () => { state: { a: 1, b: 2, c: 3 }, selector: initialProps.selector, }) + + expect(res.result.current.selectorOutput).toEqual( + res.result.current.useShallowOutput + ) }) it('returns the previously computed instance when possible', () => { @@ -226,7 +230,7 @@ describe('useShallow', () => { expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c,d') }) - it('does not cause state closure issues', () => { + it('does not cause stale closure issues', () => { const store = create((): Record => ({ a: 1, b: 2, c: 3 })) const TestShallowWithState = () => { const [count, setCount] = useState(0) From 1f1a2aec045defa5cf85e403902b010e95c71231 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 30 Sep 2023 16:06:02 +0200 Subject: [PATCH 03/12] chore(useShallow): PR feedback https://github.com/pmndrs/zustand/pull/2090#discussion_r1341963105 --- src/shallow.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/shallow.ts b/src/shallow.ts index c8acd6d26a..be50ac7580 100644 --- a/src/shallow.ts +++ b/src/shallow.ts @@ -65,13 +65,10 @@ export default ((objA, objB) => { export function useShallow(selector: (state: S) => U): (state: S) => U { const prev = useRef() - return useMemo( - () => (state) => { - const next = selector(state) - return shallow(prev.current, next) - ? (prev.current as U) - : (prev.current = next) - }, - [selector] - ) + return (state) => { + const next = selector(state) + return shallow(prev.current, next) + ? (prev.current as U) + : (prev.current = next) + } } From f4fa7d4ddf6fd9459d4d6dbc3982801adc86dc5d Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 30 Sep 2023 16:47:09 +0200 Subject: [PATCH 04/12] fix(useShallow): tests not working on test_matrix (cjs, production, CI-MATRIX-NOSKIP) --- tests/shallow.test.tsx | 116 +++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 45 deletions(-) diff --git a/tests/shallow.test.tsx b/tests/shallow.test.tsx index ce0f8c71e4..b3d0bbdc5f 100644 --- a/tests/shallow.test.tsx +++ b/tests/shallow.test.tsx @@ -1,6 +1,6 @@ import { useState } from 'react' -import { act, fireEvent, render, renderHook } from '@testing-library/react' -import { describe, expect, it } from 'vitest' +import { act, fireEvent, render } from '@testing-library/react' +import { beforeEach, describe, expect, it, vi } from 'vitest' import { create } from 'zustand' import { useStore } from 'zustand/react' import { shallow, useShallow } from 'zustand/shallow' @@ -136,63 +136,89 @@ describe('unsupported cases', () => { }) describe('useShallow', () => { - const useTestShallowSimple = (props: { - selector: (state: S) => U - state: S - }): { selectorOutput: U; useShallowOutput: U } => { - const useShallowOutput = useShallow(props.selector)(props.state) - return { - selectorOutput: props.selector(props.state), - useShallowOutput, - } + const testUseShallowSimpleCallback = + vi.fn<[{ selectorOutput: string[]; useShallowOutput: string[] }]>() + const TestUseShallowSimple = ({ + selector, + state, + }: { + state: Record + selector: (state: Record) => string[] + }) => { + const selectorOutput = selector(state) + const useShallowOutput = useShallow(selector)(state) + + return ( +
+ testUseShallowSimpleCallback({ selectorOutput, useShallowOutput }) + } + /> + ) } - it('input and output selectors always return shallow equal values', () => { - const initialProps = { - selector: (state: Record) => Object.keys(state), - state: { a: 1, b: 2 } as Record, - } - - const res = renderHook((props) => useTestShallowSimple(props), { - initialProps, - }) + beforeEach(() => { + testUseShallowSimpleCallback.mockClear() + }) - expect(res.result.current.selectorOutput).toEqual( - res.result.current.useShallowOutput + it('input and output selectors always return shallow equal values', () => { + const res = render( + ) - res.rerender({ - state: { a: 1, b: 2, c: 3 }, - selector: initialProps.selector, - }) + expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(0) + fireEvent.click(res.getByTestId('test-shallow')) - expect(res.result.current.selectorOutput).toEqual( - res.result.current.useShallowOutput + expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(1) + expect(testUseShallowSimpleCallback.mock.lastCall?.[0]).toBeTruthy() + expect( + testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput + ).toEqual(testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput) + + res.rerender( + ) + + fireEvent.click(res.getByTestId('test-shallow')) + expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(2) + expect(testUseShallowSimpleCallback.mock.lastCall?.[0]).toBeTruthy() + expect( + testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput + ).toEqual(testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput) }) it('returns the previously computed instance when possible', () => { - const initialProps = { - selector: Object.keys, - state: { a: 1, b: 2 } as Record, - } - - const res = renderHook((props) => useTestShallowSimple(props), { - initialProps, - }) + const state = { a: 1, b: 2 } + const res = render( + + ) - const output1 = res.result.current.useShallowOutput + fireEvent.click(res.getByTestId('test-shallow')) + expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(1) + const output1 = + testUseShallowSimpleCallback.mock.lastCall?.[0]?.useShallowOutput + expect(output1).toBeTruthy() + + // Change selector, same output + res.rerender( + Object.keys(state)} + /> + ) - res.rerender({ - state: initialProps.state, - selector: (state: Record) => Object.keys(state), // New selector instance - }) - const output2 = res.result.current.useShallowOutput + fireEvent.click(res.getByTestId('test-shallow')) + expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(2) - expect(output1).toBe(output2) - res.rerender(initialProps) + const output2 = + testUseShallowSimpleCallback.mock.lastCall?.[0]?.useShallowOutput + expect(output2).toBeTruthy() - expect(res.result.current.useShallowOutput).toBe(output1) + expect(output2).toBe(output1) }) it('only re-renders if selector output has changed according to shallow', () => { From 96c7c1706237479d7415bfa1d2c0eb2ce23733d9 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 1 Oct 2023 10:08:38 +0200 Subject: [PATCH 05/12] chore(useShallow): fix eslint warning issue (unused import) --- src/shallow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shallow.ts b/src/shallow.ts index be50ac7580..f64c8ca80a 100644 --- a/src/shallow.ts +++ b/src/shallow.ts @@ -1,4 +1,4 @@ -import { useMemo, useRef } from 'react' +import { useRef } from 'react' export function shallow(objA: T, objB: T) { if (Object.is(objA, objB)) { From dabaa092b8e10f7ee4b6d5b41db255572445225e Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 1 Oct 2023 10:12:23 +0200 Subject: [PATCH 06/12] refactor(useShallow): simplify tests --- tests/shallow.test.tsx | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/tests/shallow.test.tsx b/tests/shallow.test.tsx index b3d0bbdc5f..512cbed710 100644 --- a/tests/shallow.test.tsx +++ b/tests/shallow.test.tsx @@ -2,7 +2,6 @@ import { useState } from 'react' import { act, fireEvent, render } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { create } from 'zustand' -import { useStore } from 'zustand/react' import { shallow, useShallow } from 'zustand/shallow' describe('shallow', () => { @@ -170,11 +169,11 @@ describe('useShallow', () => { expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(0) fireEvent.click(res.getByTestId('test-shallow')) + const firstRender = testUseShallowSimpleCallback.mock.lastCall?.[0] + expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(1) - expect(testUseShallowSimpleCallback.mock.lastCall?.[0]).toBeTruthy() - expect( - testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput - ).toEqual(testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput) + expect(firstRender).toBeTruthy() + expect(firstRender?.selectorOutput).toEqual(firstRender?.useShallowOutput) res.rerender( { fireEvent.click(res.getByTestId('test-shallow')) expect(testUseShallowSimpleCallback).toHaveBeenCalledTimes(2) - expect(testUseShallowSimpleCallback.mock.lastCall?.[0]).toBeTruthy() - expect( - testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput - ).toEqual(testUseShallowSimpleCallback.mock.lastCall?.[0]?.selectorOutput) + + const secondRender = testUseShallowSimpleCallback.mock.lastCall?.[0] + + expect(secondRender).toBeTruthy() + expect(secondRender?.selectorOutput).toEqual(secondRender?.useShallowOutput) }) it('returns the previously computed instance when possible', () => { @@ -223,13 +223,15 @@ describe('useShallow', () => { it('only re-renders if selector output has changed according to shallow', () => { let countRenders = 0 - const store = create((): Record => ({ a: 1, b: 2, c: 3 })) + const useMyStore = create( + (): Record => ({ a: 1, b: 2, c: 3 }) + ) const TestShallow = ({ selector = (state) => Object.keys(state).sort(), }: { selector?: (state: Record) => string[] }) => { - const output = useStore(store, useShallow(selector)) + const output = useMyStore(useShallow(selector)) ++countRenders @@ -243,13 +245,13 @@ describe('useShallow', () => { expect(res.getByTestId('test-shallow').textContent).toBe('a,b,c') act(() => { - store.setState({ a: 4 }) // This will not cause a re-render. + useMyStore.setState({ a: 4 }) // This will not cause a re-render. }) expect(countRenders).toBe(1) act(() => { - store.setState({ d: 10 }) // This will cause a re-render. + useMyStore.setState({ d: 10 }) // This will cause a re-render. }) expect(countRenders).toBe(2) @@ -257,11 +259,12 @@ describe('useShallow', () => { }) it('does not cause stale closure issues', () => { - const store = create((): Record => ({ a: 1, b: 2, c: 3 })) + const useMyStore = create( + (): Record => ({ a: 1, b: 2, c: 3 }) + ) const TestShallowWithState = () => { const [count, setCount] = useState(0) - const output = useStore( - store, + const output = useMyStore( useShallow((state) => Object.keys(state).concat([count.toString()])) ) From 9526d87ab80b0ebb4a1260c5ab66def886f72ac2 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 1 Oct 2023 10:57:41 +0200 Subject: [PATCH 07/12] docs(useShallow): add guide --- .../prevent-rerenders-with-use-shallow.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/guides/prevent-rerenders-with-use-shallow.md diff --git a/docs/guides/prevent-rerenders-with-use-shallow.md b/docs/guides/prevent-rerenders-with-use-shallow.md new file mode 100644 index 0000000000..b20b071f39 --- /dev/null +++ b/docs/guides/prevent-rerenders-with-use-shallow.md @@ -0,0 +1,64 @@ +--- +title: Prevent rerenders with useShallow +nav: 16 +--- + +When you need to subscribe to a computed state from a store, the recommended way is to +use a selector. + +The computed selector will cause a rererender if the output has changed according to [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is?retiredLocale=it). + +In this case you might want to use `useShallow` to avoid a rerender if the computed value is always shallow +equal the previous one. + +## Example + +We have a store that associate to each bear a meal and we want to render their names. + +```js +import { create } from 'zustand' + +const useMeals = create(() => ({ + papaBear: 'large porridge-pot', + mamaBear: 'middle-size porridge pot', + littleBear: 'A little, small, wee pot', +})) + +export const BearNames = () => { + const names = useMeals((state) => Object.keys(state)) + + return
{names.join(', ')}
+} +``` + +Now papa bear wants a pizza instead: + +```js +useMeals.setState({ + papaBear: 'a large pizza', +}) +``` + +This change causes `BearNames` rerenders even tho the actual output of `names` has not changed according to shallow equal. + +We can fix that using `useShallow`! + +```js +import { create } from 'zustand' +import { useShallow } from 'zustand/shallow' + +const useMeals = create(() => ({ + papaBear: 'large porridge-pot', + mamaBear: 'middle-size porridge pot', + littleBear: 'A little, small, wee pot', +})) + +export const BearNames = () => { + const names = useMeals(useShallow((state) => Object.keys(state))) + + return
{names.join(', ')}
+} +``` + +Now they can all order other meals without causing unnecessary rerenders of `BearNames`. + From ac28c31c708bbb5f90c5f9b09779c23aa2e659eb Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 1 Oct 2023 11:29:42 +0200 Subject: [PATCH 08/12] fix(useShallow): prettier:ci error https://github.com/pmndrs/zustand/actions/runs/6369420511/job/17289749161?pr=2090 --- docs/guides/prevent-rerenders-with-use-shallow.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/guides/prevent-rerenders-with-use-shallow.md b/docs/guides/prevent-rerenders-with-use-shallow.md index b20b071f39..c3b5ede6d3 100644 --- a/docs/guides/prevent-rerenders-with-use-shallow.md +++ b/docs/guides/prevent-rerenders-with-use-shallow.md @@ -61,4 +61,3 @@ export const BearNames = () => { ``` Now they can all order other meals without causing unnecessary rerenders of `BearNames`. - From 71605274466cc4eec3ee19e32401671c1b3cf963 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 1 Oct 2023 11:33:35 +0200 Subject: [PATCH 09/12] docs(useShallow): update readme --- readme.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/readme.md b/readme.md index 10e369ad6d..adfa0a71e7 100644 --- a/readme.md +++ b/readme.md @@ -86,36 +86,31 @@ const honey = useBearStore((state) => state.honey) If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can tell zustand that you want the object to be diffed shallowly by passing the `shallow` equality function. -To use a custom equality function, you need `createWithEqualityFn` instead of `create`. Usually you want to specify `Object.is` as the second argument for the default equality function, but it's configurable. +To prevent unnecessary rerenders when the selector output does not change according to shallow equal, you can use [useShallow](./docs/guides/prevent-rerenders-with-use-shallow.md). ```jsx -import { createWithEqualityFn } from 'zustand/traditional' -import { shallow } from 'zustand/shallow' +import { create } from 'zustand' +import { useShallow } from 'zustand/shallow' // Use createWithEqualityFn instead of create -const useBearStore = createWithEqualityFn( - (set) => ({ - bears: 0, - increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), - removeAllBears: () => set({ bears: 0 }), - }), - Object.is // Specify the default equality function, which can be shallow -) +const useBearStore = create((set) => ({ + bears: 0, + increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), + removeAllBears: () => set({ bears: 0 }), +})) // Object pick, re-renders the component when either state.nuts or state.honey change const { nuts, honey } = useBearStore( - (state) => ({ nuts: state.nuts, honey: state.honey }), - shallow + useShallow((state) => ({ nuts: state.nuts, honey: state.honey })) ) // Array pick, re-renders the component when either state.nuts or state.honey change const [nuts, honey] = useBearStore( - (state) => [state.nuts, state.honey], - shallow + useShallow((state) => [state.nuts, state.honey]) ) // Mapped picks, re-renders the component when state.treats changes in order, count or keys -const treats = useBearStore((state) => Object.keys(state.treats), shallow) +const treats = useBearStore(useShallow((state) => Object.keys(state.treats))) ``` For more control over re-rendering, you may provide any custom equality function. From 323291c505f50e6fc4dc0d66a3d1991328e18287 Mon Sep 17 00:00:00 2001 From: Fabrizio Vitale Date: Sun, 1 Oct 2023 18:45:20 +0200 Subject: [PATCH 10/12] docs(useShallow): remove obsolete line from readme Co-authored-by: Daishi Kato --- readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.md b/readme.md index adfa0a71e7..c13ef802d0 100644 --- a/readme.md +++ b/readme.md @@ -92,7 +92,6 @@ To prevent unnecessary rerenders when the selector output does not change accord import { create } from 'zustand' import { useShallow } from 'zustand/shallow' -// Use createWithEqualityFn instead of create const useBearStore = create((set) => ({ bears: 0, increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), From 6ae26e5054385e96c35e3ccbb4c2306ea39b72ff Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 1 Oct 2023 18:52:44 +0200 Subject: [PATCH 11/12] doc(useShallow): PR feedback https://github.com/pmndrs/zustand/pull/2090#discussion_r1342120701 --- readme.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/readme.md b/readme.md index c13ef802d0..91da872cbd 100644 --- a/readme.md +++ b/readme.md @@ -84,9 +84,7 @@ const nuts = useBearStore((state) => state.nuts) const honey = useBearStore((state) => state.honey) ``` -If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can tell zustand that you want the object to be diffed shallowly by passing the `shallow` equality function. - -To prevent unnecessary rerenders when the selector output does not change according to shallow equal, you can use [useShallow](./docs/guides/prevent-rerenders-with-use-shallow.md). +If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can use [useShallow](./docs/guides/prevent-rerenders-with-use-shallow.md) to prevent unnecessary rerenders when the selector output does not change according to shallow equal. ```jsx import { create } from 'zustand' From 05aca1894eaac196bd43d90d949c6e132e0252f8 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sun, 1 Oct 2023 18:55:00 +0200 Subject: [PATCH 12/12] docs(useShallow): small improvements of the useShallow guide --- docs/guides/prevent-rerenders-with-use-shallow.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/prevent-rerenders-with-use-shallow.md b/docs/guides/prevent-rerenders-with-use-shallow.md index c3b5ede6d3..c8e667dbb5 100644 --- a/docs/guides/prevent-rerenders-with-use-shallow.md +++ b/docs/guides/prevent-rerenders-with-use-shallow.md @@ -13,7 +13,7 @@ equal the previous one. ## Example -We have a store that associate to each bear a meal and we want to render their names. +We have a store that associates to each bear a meal and we want to render their names. ```js import { create } from 'zustand' @@ -60,4 +60,4 @@ export const BearNames = () => { } ``` -Now they can all order other meals without causing unnecessary rerenders of `BearNames`. +Now they can all order other meals without causing unnecessary rerenders of our `BearNames` component.