main
js 133 lines 2.89 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 * as React from 'react';
11
12 const {useState, useEffect, useSyncExternalStore} = React;
13
14 // Create a simple external store for demonstratio
15 function createStore<T>(initialValue: T): {
16 subscribe: (cb: () => void) => () => any,
17 getSnapshot: () => T,
18 setValue: (newValue: T) => void,
19 } {
20 let value = initialValue;
21 const subscribers = new Set<() => void>();
22
23 return {
24 subscribe(callback) {
25 subscribers.add(callback);
26 return () => subscribers.delete(callback);
27 },
28 getSnapshot() {
29 return value;
30 },
31 setValue(newValue) {
32 value = newValue;
33 subscribers.forEach(callback => callback());
34 },
35 };
36 }
37
38 const counterStore = createStore(0);
39 const themeStore = createStore('light');
40
41 export default function UseSyncExternalStore(): React.Node {
42 return (
43 <>
44 <h2>useSyncExternalStore()</h2>
45 <SingleHookCase />
46 <HookTreeCase />
47 <MultipleStoresCase />
48 </>
49 );
50 }
51
52 function SingleHookCase(): React.Node {
53 const count = useSyncExternalStore(
54 counterStore.subscribe,
55 counterStore.getSnapshot,
56 );
57
58 return (
59 <div>
60 <h3>Single hook case</h3>
61 <p>Count: {count}</p>
62 <button onClick={() => counterStore.setValue(count + 1)}>
63 Increment
64 </button>
65 <button onClick={() => counterStore.setValue(count - 1)}>
66 Decrement
67 </button>
68 </div>
69 );
70 }
71
72 function useCounter() {
73 const count = useSyncExternalStore(
74 counterStore.subscribe,
75 counterStore.getSnapshot,
76 );
77 const [localState, setLocalState] = useState(0);
78
79 useEffect(() => {
80 // Some effect
81 }, [count]);
82
83 return {count, localState, setLocalState};
84 }
85
86 function HookTreeCase(): React.Node {
87 const {count, localState, setLocalState} = useCounter();
88
89 return (
90 <div>
91 <h3>Hook tree case</h3>
92 <p>External count: {count}</p>
93 <p>Local state: {localState}</p>
94 <button onClick={() => counterStore.setValue(count + 1)}>
95 Increment External
96 </button>
97 <button onClick={() => setLocalState(localState + 1)}>
98 Increment Local
99 </button>
100 </div>
101 );
102 }
103
104 function useTheme() {
105 const theme = useSyncExternalStore(
106 themeStore.subscribe,
107 themeStore.getSnapshot,
108 );
109
110 return theme;
111 }
112
113 function MultipleStoresCase() {
114 const count = useSyncExternalStore(
115 counterStore.subscribe,
116 counterStore.getSnapshot,
117 );
118 const theme = useTheme();
119
120 return (
121 <div style={{background: theme === 'dark' ? '#333' : '#fff'}}>
122 <h3>Multiple stores case</h3>
123 <p>Count: {count}</p>
124 <p>Theme: {theme}</p>
125 <button
126 onClick={() =>
127 themeStore.setValue(theme === 'light' ? 'dark' : 'light')
128 }>
129 Toggle Theme
130 </button>
131 </div>
132 );
133 }