1 | 'use client';
|
2 |
|
3 | import _extends from "@babel/runtime/helpers/esm/extends";
|
4 | import * as React from 'react';
|
5 | function areEqual(a, b) {
|
6 | return a === b;
|
7 | }
|
8 | const EMPTY_OBJECT = {};
|
9 | const NOOP = () => {};
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | function getControlledState(internalState, controlledProps) {
|
16 | const augmentedState = _extends({}, internalState);
|
17 | Object.keys(controlledProps).forEach(key => {
|
18 | if (controlledProps[key] !== undefined) {
|
19 | augmentedState[key] = controlledProps[key];
|
20 | }
|
21 | });
|
22 | return augmentedState;
|
23 | }
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | function useStateChangeDetection(parameters) {
|
30 | const {
|
31 | nextState,
|
32 | initialState,
|
33 | stateComparers,
|
34 | onStateChange,
|
35 | controlledProps,
|
36 | lastActionRef
|
37 | } = parameters;
|
38 | const internalPreviousStateRef = React.useRef(initialState);
|
39 | React.useEffect(() => {
|
40 | if (lastActionRef.current === null) {
|
41 |
|
42 | return;
|
43 | }
|
44 | const previousState = getControlledState(internalPreviousStateRef.current, controlledProps);
|
45 | Object.keys(nextState).forEach(key => {
|
46 |
|
47 | const stateComparer = stateComparers[key] ?? areEqual;
|
48 | const nextStateItem = nextState[key];
|
49 | const previousStateItem = previousState[key];
|
50 | if (previousStateItem == null && nextStateItem != null || previousStateItem != null && nextStateItem == null || previousStateItem != null && nextStateItem != null && !stateComparer(nextStateItem, previousStateItem)) {
|
51 | onStateChange?.(lastActionRef.current.event ?? null, key, nextStateItem, lastActionRef.current.type ?? '', nextState);
|
52 | }
|
53 | });
|
54 | internalPreviousStateRef.current = nextState;
|
55 | lastActionRef.current = null;
|
56 | }, [internalPreviousStateRef, nextState, lastActionRef, onStateChange, stateComparers, controlledProps]);
|
57 | }
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | export function useControllableReducer(parameters) {
|
84 | const lastActionRef = React.useRef(null);
|
85 | const {
|
86 | reducer,
|
87 | initialState,
|
88 | controlledProps = EMPTY_OBJECT,
|
89 | stateComparers = EMPTY_OBJECT,
|
90 | onStateChange = NOOP,
|
91 | actionContext,
|
92 | componentName = ''
|
93 | } = parameters;
|
94 | const controlledPropsRef = React.useRef(controlledProps);
|
95 | if (process.env.NODE_ENV !== 'production') {
|
96 |
|
97 | React.useEffect(() => {
|
98 | Object.keys(controlledProps).forEach(key => {
|
99 | if (controlledPropsRef.current[key] !== undefined && controlledProps[key] === undefined) {
|
100 | console.error(`useControllableReducer: ${componentName ? `The ${componentName} component` : 'A component'} is changing a controlled prop to be uncontrolled: ${key}`);
|
101 | }
|
102 | if (controlledPropsRef.current[key] === undefined && controlledProps[key] !== undefined) {
|
103 | console.error(`useControllableReducer: ${componentName ? `The ${componentName} component` : 'A component'} is changing an uncontrolled prop to be controlled: ${key}`);
|
104 | }
|
105 | });
|
106 | }, [controlledProps, componentName]);
|
107 | }
|
108 |
|
109 |
|
110 | const reducerWithControlledState = React.useCallback((state, action) => {
|
111 | lastActionRef.current = action;
|
112 | const controlledState = getControlledState(state, controlledProps);
|
113 | const newState = reducer(controlledState, action);
|
114 | return newState;
|
115 | }, [controlledProps, reducer]);
|
116 | const [nextState, dispatch] = React.useReducer(reducerWithControlledState, initialState);
|
117 |
|
118 |
|
119 | const dispatchWithContext = React.useCallback(action => {
|
120 | dispatch(_extends({}, action, {
|
121 | context: actionContext
|
122 | }));
|
123 | }, [actionContext]);
|
124 | useStateChangeDetection({
|
125 | nextState,
|
126 | initialState,
|
127 | stateComparers: stateComparers ?? EMPTY_OBJECT,
|
128 | onStateChange: onStateChange ?? NOOP,
|
129 | controlledProps,
|
130 | lastActionRef
|
131 | });
|
132 | return [getControlledState(nextState, controlledProps), dispatchWithContext];
|
133 | } |
\ | No newline at end of file |