Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
slorber committed Jun 2, 2022
1 parent 2eb5951 commit df58da0
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import React, {
type ReactNode,
type ComponentType,
} from 'react';
import {ReactContextError} from '../../utils/reactUtils';
import useShallowMemoObject from '../../hooks/useShallowMemoObject';
import {ReactContextError, useShallowMemoObject} from '../../utils/reactUtils';

// This context represents a "global layout store". A component (usually a
// layout component) can request filling this store through
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {useEffect} from 'react';
import {useDynamicCallback} from '../utils/reactUtils';
import useShallowMemoObject from './useShallowMemoObject';
import {useDynamicCallback, useShallowMemoObject} from '../utils/reactUtils';

type Options = MutationObserverInit;

Expand Down
17 changes: 0 additions & 17 deletions packages/docusaurus-theme-common/src/hooks/useShallowMemoObject.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import {renderHook} from '@testing-library/react-hooks';
import {usePrevious} from '../reactUtils';
import {usePrevious, useShallowMemoObject} from '../reactUtils';

describe('usePrevious', () => {
it('returns the previous value of a variable', () => {
Expand All @@ -20,3 +20,37 @@ describe('usePrevious', () => {
expect(result.current).toBe(2);
});
});

describe('useShallowMemoObject', () => {
it('can memoize object', () => {
const someObj = {hello: 'world'};
const someArray = ['hello', 'world'];

const obj1 = {a: 1, b: '2', someObj, someArray};
const {result, rerender} = renderHook((val) => useShallowMemoObject(val), {
initialProps: obj1,
});
expect(result.current).toBe(obj1);

const obj2 = {a: 1, b: '2', someObj, someArray};
rerender(obj2);
expect(result.current).toBe(obj1);

const obj3 = {a: 1, b: '2', someObj, someArray};
rerender(obj3);
expect(result.current).toBe(obj1);

// Current implementation is basic and sensitive to order
const obj4 = {b: '2', a: 1, someObj, someArray};
rerender(obj4);
expect(result.current).toBe(obj4);

const obj5 = {b: '2', a: 1, someObj, someArray};
rerender(obj5);
expect(result.current).toBe(obj4);

const obj6 = {b: '2', a: 1, someObj: {...someObj}, someArray};
rerender(obj6);
expect(result.current).toBe(obj6);
});
});
22 changes: 21 additions & 1 deletion packages/docusaurus-theme-common/src/utils/reactUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {useCallback, useEffect, useLayoutEffect, useRef} from 'react';
import {useCallback, useEffect, useLayoutEffect, useMemo, useRef} from 'react';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';

/**
Expand Down Expand Up @@ -74,3 +74,23 @@ export class ReactContextError extends Error {
} is called outside the <${providerName}>. ${additionalInfo ?? ''}`;
}
}

/**
* Shallow-memoize an object
*
* This means the returned object will be the same as the previous render
* if the attribute names and identities did not change.
*
* This works for simple cases: when attributes are primitives or stable objects
*
* @param obj
*/
export function useShallowMemoObject<O>(obj: O): O {
return useMemo(
() => obj,
// Is this safe?
// TODO make this implementation not order-dependent?
// eslint-disable-next-line react-hooks/exhaustive-deps
[...Object.keys(obj), ...Object.values(obj)],
);
}

0 comments on commit df58da0

Please sign in to comment.