|
1 | 1 | import { supportRef } from 'rc-util/lib/ref'; |
2 | 2 | import * as React from 'react'; |
3 | 3 |
|
4 | | -const ImmutableContext = React.createContext<number>(0); |
5 | | - |
6 | 4 | export type CompareProps<T extends React.ComponentType<any>> = ( |
7 | 5 | prevProps: Readonly<React.ComponentProps<T>>, |
8 | 6 | nextProps: Readonly<React.ComponentProps<T>>, |
9 | 7 | ) => boolean; |
10 | 8 |
|
11 | 9 | /** |
12 | | - * Get render update mark by `makeImmutable` root. |
13 | | - * Do not deps on the return value as render times |
14 | | - * but only use for `useMemo` or `useCallback` deps. |
| 10 | + * Create Immutable pair for `makeImmutable` and `responseImmutable`. |
15 | 11 | */ |
16 | | -export function useImmutableMark() { |
17 | | - return React.useContext(ImmutableContext); |
18 | | -} |
| 12 | +export default function createImmutable() { |
| 13 | + const ImmutableContext = React.createContext<number>(null); |
| 14 | + |
| 15 | + /** |
| 16 | + * Get render update mark by `makeImmutable` root. |
| 17 | + * Do not deps on the return value as render times |
| 18 | + * but only use for `useMemo` or `useCallback` deps. |
| 19 | + */ |
| 20 | + function useImmutableMark() { |
| 21 | + return React.useContext(ImmutableContext); |
| 22 | + } |
19 | 23 |
|
20 | | -/** |
| 24 | + /** |
21 | 25 | * Wrapped Component will be marked as Immutable. |
22 | 26 | * When Component parent trigger render, |
23 | 27 | * it will notice children component (use with `responseImmutable`) node that parent has updated. |
24 | 28 |
|
25 | 29 | * @param Component Passed Component |
26 | 30 | * @param triggerRender Customize trigger `responseImmutable` children re-render logic. Default will always trigger re-render when this component re-render. |
27 | 31 | */ |
28 | | -export function makeImmutable<T extends React.ComponentType<any>>( |
29 | | - Component: T, |
30 | | - shouldTriggerRender?: CompareProps<T>, |
31 | | -): T { |
32 | | - const refAble = supportRef(Component); |
33 | | - |
34 | | - const ImmutableComponent = function (props: any, ref: any) { |
35 | | - const refProps = refAble ? { ref } : {}; |
36 | | - const renderTimesRef = React.useRef(0); |
37 | | - const prevProps = React.useRef(props); |
38 | | - |
39 | | - if ( |
40 | | - // Always trigger re-render if not provide `notTriggerRender` |
41 | | - !shouldTriggerRender || |
42 | | - shouldTriggerRender(prevProps.current, props) |
43 | | - ) { |
44 | | - renderTimesRef.current += 1; |
| 32 | + function makeImmutable<T extends React.ComponentType<any>>( |
| 33 | + Component: T, |
| 34 | + shouldTriggerRender?: CompareProps<T>, |
| 35 | + ): T { |
| 36 | + const refAble = supportRef(Component); |
| 37 | + |
| 38 | + const ImmutableComponent = function (props: any, ref: any) { |
| 39 | + const refProps = refAble ? { ref } : {}; |
| 40 | + const renderTimesRef = React.useRef(0); |
| 41 | + const prevProps = React.useRef(props); |
| 42 | + |
| 43 | + // If parent has the context, we do not wrap it |
| 44 | + const mark = useImmutableMark(); |
| 45 | + if (mark !== null) { |
| 46 | + return <Component {...props} {...refProps} />; |
| 47 | + } |
| 48 | + |
| 49 | + if ( |
| 50 | + // Always trigger re-render if not provide `notTriggerRender` |
| 51 | + !shouldTriggerRender || |
| 52 | + shouldTriggerRender(prevProps.current, props) |
| 53 | + ) { |
| 54 | + renderTimesRef.current += 1; |
| 55 | + } |
| 56 | + |
| 57 | + prevProps.current = props; |
| 58 | + |
| 59 | + return ( |
| 60 | + <ImmutableContext.Provider value={renderTimesRef.current}> |
| 61 | + <Component {...props} {...refProps} /> |
| 62 | + </ImmutableContext.Provider> |
| 63 | + ); |
| 64 | + }; |
| 65 | + |
| 66 | + if (process.env.NODE_ENV !== 'production') { |
| 67 | + ImmutableComponent.displayName = `ImmutableRoot(${Component.displayName || Component.name})`; |
45 | 68 | } |
46 | 69 |
|
47 | | - prevProps.current = props; |
48 | | - |
49 | | - return ( |
50 | | - <ImmutableContext.Provider value={renderTimesRef.current}> |
51 | | - <Component {...props} {...refProps} /> |
52 | | - </ImmutableContext.Provider> |
53 | | - ); |
54 | | - }; |
55 | | - |
56 | | - if (process.env.NODE_ENV !== 'production') { |
57 | | - ImmutableComponent.displayName = `ImmutableRoot(${Component.displayName || Component.name})`; |
| 70 | + return refAble ? React.forwardRef(ImmutableComponent) : (ImmutableComponent as any); |
58 | 71 | } |
59 | 72 |
|
60 | | - return refAble ? React.forwardRef(ImmutableComponent) : (ImmutableComponent as any); |
61 | | -} |
62 | | - |
63 | | -/** |
64 | | - * Wrapped Component with `React.memo`. |
65 | | - * But will rerender when parent with `makeImmutable` rerender. |
66 | | - */ |
67 | | -export function responseImmutable<T extends React.ComponentType<any>>( |
68 | | - Component: T, |
69 | | - propsAreEqual?: CompareProps<T>, |
70 | | -): T { |
71 | | - const refAble = supportRef(Component); |
72 | | - |
73 | | - const ImmutableComponent = function (props: any, ref: any) { |
74 | | - const refProps = refAble ? { ref } : {}; |
75 | | - useImmutableMark(); |
76 | | - |
77 | | - return <Component {...props} {...refProps} />; |
78 | | - }; |
| 73 | + /** |
| 74 | + * Wrapped Component with `React.memo`. |
| 75 | + * But will rerender when parent with `makeImmutable` rerender. |
| 76 | + */ |
| 77 | + function responseImmutable<T extends React.ComponentType<any>>( |
| 78 | + Component: T, |
| 79 | + propsAreEqual?: CompareProps<T>, |
| 80 | + ): T { |
| 81 | + const refAble = supportRef(Component); |
| 82 | + |
| 83 | + const ImmutableComponent = function (props: any, ref: any) { |
| 84 | + const refProps = refAble ? { ref } : {}; |
| 85 | + useImmutableMark(); |
| 86 | + |
| 87 | + return <Component {...props} {...refProps} />; |
| 88 | + }; |
| 89 | + |
| 90 | + if (process.env.NODE_ENV !== 'production') { |
| 91 | + ImmutableComponent.displayName = `ImmutableResponse(${ |
| 92 | + Component.displayName || Component.name |
| 93 | + })`; |
| 94 | + } |
79 | 95 |
|
80 | | - if (process.env.NODE_ENV !== 'production') { |
81 | | - ImmutableComponent.displayName = `ImmutableResponse(${ |
82 | | - Component.displayName || Component.name |
83 | | - })`; |
| 96 | + return refAble |
| 97 | + ? React.memo(React.forwardRef(ImmutableComponent), propsAreEqual) |
| 98 | + : (React.memo(ImmutableComponent, propsAreEqual) as any); |
84 | 99 | } |
85 | 100 |
|
86 | | - return refAble |
87 | | - ? React.memo(React.forwardRef(ImmutableComponent), propsAreEqual) |
88 | | - : (React.memo(ImmutableComponent, propsAreEqual) as any); |
| 101 | + return { |
| 102 | + makeImmutable, |
| 103 | + responseImmutable, |
| 104 | + useImmutableMark, |
| 105 | + }; |
89 | 106 | } |
0 commit comments