main
js 175 lines 4.43 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {ReactContext} from 'shared/ReactTypes';
11 import * as React from 'react';
12 import {
13 createContext,
14 Component,
15 forwardRef,
16 Fragment,
17 memo,
18 useCallback,
19 useDebugValue,
20 useEffect,
21 useReducer,
22 useState,
23 } from 'react';
24
25 const initialData = {foo: 'FOO', bar: 'BAR'};
26
27 // $FlowFixMe[missing-local-annot]
28 function reducer(state, action: {type: string}) {
29 switch (action.type) {
30 case 'swap':
31 return {foo: state.bar, bar: state.foo};
32 default:
33 throw new Error();
34 }
35 }
36
37 type StatefulFunctionProps = {name: string};
38
39 function StatefulFunction({name}: StatefulFunctionProps) {
40 const [count, updateCount] = useState(0);
41 const debouncedCount = useDebounce(count, 1000);
42 const handleUpdateCountClick = useCallback(
43 () => updateCount(count + 1),
44 [count],
45 );
46
47 const [data, dispatch] = useReducer(reducer, initialData);
48 const handleUpdateReducerClick = useCallback(
49 () => dispatch({type: 'swap'}),
50 [],
51 );
52
53 return (
54 <ul>
55 <li>Name: {name}</li>
56 <li>
57 <button onClick={handleUpdateCountClick}>
58 Debounced count: {debouncedCount}
59 </button>
60 </li>
61 <li>
62 Reducer state: foo "{data.foo}", bar "{data.bar}"
63 </li>
64 <li>
65 <button onClick={handleUpdateReducerClick}>Swap reducer values</button>
66 </li>
67 </ul>
68 );
69 }
70
71 const BoolContext = createContext(true);
72 BoolContext.displayName = 'BoolContext';
73
74 type Props = {name: string, toggle: boolean};
75 type State = {cities: Array<string>, state: string};
76
77 class StatefulClass extends Component<Props, State> {
78 static contextType: ReactContext<boolean> = BoolContext;
79
80 state: State = {
81 cities: ['San Francisco', 'San Jose'],
82 state: 'California',
83 };
84
85 // $FlowFixMe[missing-local-annot]
86 handleChange = ({target}): any =>
87 this.setState({
88 state: target.value,
89 });
90
91 render(): any {
92 return (
93 <ul>
94 <li>Name: {this.props.name}</li>
95 <li>Toggle: {this.props.toggle ? 'true' : 'false'}</li>
96 <li>
97 State: <input value={this.state.state} onChange={this.handleChange} />
98 </li>
99 <li>Cities: {this.state.cities.join(', ')}</li>
100 <li>Context: {this.context ? 'true' : 'false'}</li>
101 </ul>
102 );
103 }
104 }
105
106 const MemoizedStatefulClass = memo(StatefulClass);
107 const MemoizedStatefulFunction = memo(StatefulFunction);
108
109 const ForwardRef = forwardRef<{name: string}, HTMLUListElement>(
110 ({name}, ref) => {
111 const [count, updateCount] = useState(0);
112 const debouncedCount = useDebounce(count, 1000);
113 const handleUpdateCountClick = useCallback(
114 () => updateCount(count + 1),
115 [count],
116 );
117 return (
118 <ul ref={ref}>
119 <li>Name: {name}</li>
120 <li>
121 <button onClick={handleUpdateCountClick}>
122 Debounced count: {debouncedCount}
123 </button>
124 </li>
125 </ul>
126 );
127 },
128 );
129
130 export default function EditableProps(): React.Node {
131 return (
132 <Fragment>
133 <h1>Editable props</h1>
134 <strong>Class</strong>
135 <StatefulClass name="Brian" toggle={true} />
136 <strong>Function</strong>
137 <StatefulFunction name="Brian" />
138 <strong>Memoized Class</strong>
139 <MemoizedStatefulClass name="Brian" toggle={true} />
140 <strong>Memoized Function</strong>
141 <MemoizedStatefulFunction name="Brian" />
142 <strong>Forward Ref</strong>
143 <ForwardRef name="Brian" />
144 </Fragment>
145 );
146 }
147
148 // Below copied from https://usehooks.com/
149 function useDebounce(value: number, delay: number) {
150 // State and setters for debounced value
151 const [debouncedValue, setDebouncedValue] = useState(value);
152
153 // Show the value in DevTools
154 useDebugValue(debouncedValue);
155
156 useEffect(
157 () => {
158 // Update debounced value after delay
159 const handler = setTimeout(() => {
160 setDebouncedValue(value);
161 }, delay);
162
163 // Cancel the timeout if value changes (also on delay change or unmount)
164 // This is how we prevent debounced value from updating if value is changed ...
165 // .. within the delay period. Timeout gets cleared and restarted.
166 return () => {
167 clearTimeout(handler);
168 };
169 },
170 [value, delay], // Only re-call effect if value or delay changes
171 );
172
173 return debouncedValue;
174 }
175 // Above copied from https://usehooks.com/