[DevTools] chore: add useSyncExternalStore examples to shell (#34932)
Few examples of using `useSyncExternalStore` that can be useful for debugging hook tree reconstruction logic and hook names parsing feature.
Ruslan Lesiutin committed
Oct 21, 2025 at 13:51 UTC
613cf80f263663f2c9923324ca41591c230c20ad
2 files changed
+135
packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js
+2
@@ -20,6 +20,7 @@ import SimpleValues from './SimpleValues';
20
import SymbolKeys from './SymbolKeys';
21
import UseMemoCache from './UseMemoCache';
22
import UseEffectEvent from './UseEffectEvent';
23
+import UseSyncExternalStore from './UseSyncExternalStore';
24
25
// TODO Add Immutable JS example
26
@@ -38,6 +39,7 @@ export default function InspectableElements(): React.Node {
39
<SymbolKeys />
40
<UseMemoCache />
41
<UseEffectEvent />
42
+ <UseSyncExternalStore />
43
</Fragment>
44
);
45
}
packages/react-devtools-shell/src/app/InspectableElements/UseSyncExternalStore.js
new
+133
@@ -0,0 +1,133 @@
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
+}