@samitouri / QOS-React / commits / 613cf80f26

[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 +}