main
js 328 lines 9.63 KB
Raw
1 /* eslint-disable react/react-in-jsx-scope, react/jsx-no-undef */
2 /* global React ReactCache ReactDOM SchedulerTracing ScheduleTracing */
3
4 const apps = [];
5
6 const pieces = React.version.split('.');
7 const major =
8 pieces[0] === '0' ? parseInt(pieces[1], 10) : parseInt(pieces[0], 10);
9 const minor =
10 pieces[0] === '0' ? parseInt(pieces[2], 10) : parseInt(pieces[1], 10);
11
12 // Convenience wrapper to organize API features in DevTools.
13 function Feature({children, label, version}) {
14 return (
15 <div className="Feature">
16 <div className="FeatureHeader">
17 <code className="FeatureCode">{label}</code>
18 <small>{version}</small>
19 </div>
20 {children}
21 </div>
22 );
23 }
24
25 // Simplify interaction tracing for tests below.
26 let trace = null;
27 if (typeof SchedulerTracing !== 'undefined') {
28 trace = SchedulerTracing.unstable_trace;
29 } else if (typeof ScheduleTracing !== 'undefined') {
30 trace = ScheduleTracing.unstable_trace;
31 } else {
32 trace = (_, __, callback) => callback();
33 }
34
35 // https://github.com/facebook/react/blob/main/CHANGELOG.md
36 switch (major) {
37 case 16:
38 switch (minor) {
39 case 7:
40 if (typeof React.useState === 'function') {
41 // Hooks
42 function Hooks() {
43 const [count, setCount] = React.useState(0);
44 const incrementCount = React.useCallback(
45 () => setCount(count + 1),
46 [count]
47 );
48 return (
49 <div>
50 count: {count}{' '}
51 <button onClick={incrementCount}>increment</button>
52 </div>
53 );
54 }
55 apps.push(
56 <Feature key="Hooks" label="Hooks" version="16.7+">
57 <Hooks />
58 </Feature>
59 );
60 }
61 case 6:
62 // memo
63 function LabelComponent({label}) {
64 return <label>{label}</label>;
65 }
66 const AnonymousMemoized = React.memo(({label}) => (
67 <label>{label}</label>
68 ));
69 const Memoized = React.memo(LabelComponent);
70 const CustomMemoized = React.memo(LabelComponent);
71 CustomMemoized.displayName = 'MemoizedLabelFunction';
72 apps.push(
73 <Feature key="memo" label="memo" version="16.6+">
74 <AnonymousMemoized label="AnonymousMemoized" />
75 <Memoized label="Memoized" />
76 <CustomMemoized label="CustomMemoized" />
77 </Feature>
78 );
79
80 // Suspense
81 const loadResource = ([text, ms]) => {
82 return new Promise((resolve, reject) => {
83 setTimeout(() => {
84 resolve(text);
85 }, ms);
86 });
87 };
88 const getResourceKey = ([text, ms]) => text;
89 const Resource = ReactCache.unstable_createResource(
90 loadResource,
91 getResourceKey
92 );
93 class Suspending extends React.Component {
94 state = {useSuspense: false};
95 useSuspense = () => this.setState({useSuspense: true});
96 render() {
97 if (this.state.useSuspense) {
98 const text = Resource.read(['loaded', 2000]);
99 return text;
100 } else {
101 return <button onClick={this.useSuspense}>load data</button>;
102 }
103 }
104 }
105 apps.push(
106 <Feature key="Suspense" label="Suspense" version="16.6+">
107 <React.Suspense fallback={<div>loading...</div>}>
108 <Suspending />
109 </React.Suspense>
110 </Feature>
111 );
112
113 // lazy
114 const LazyWithDefaultProps = React.lazy(
115 () =>
116 new Promise(resolve => {
117 function FooWithDefaultProps(props) {
118 return (
119 <h1>
120 {props.greeting}, {props.name}
121 </h1>
122 );
123 }
124 FooWithDefaultProps.defaultProps = {
125 name: 'World',
126 greeting: 'Bonjour',
127 };
128 resolve({
129 default: FooWithDefaultProps,
130 });
131 })
132 );
133 apps.push(
134 <Feature key="lazy" label="lazy" version="16.6+">
135 <React.Suspense fallback={<div>loading...</div>}>
136 <LazyWithDefaultProps greeting="Hello" />
137 </React.Suspense>
138 </Feature>
139 );
140 case 5:
141 case 4:
142 // unstable_Profiler
143 class ProfilerChild extends React.Component {
144 state = {count: 0};
145 incrementCount = () =>
146 this.setState(prevState => ({count: prevState.count + 1}));
147 render() {
148 return (
149 <div>
150 count: {this.state.count}{' '}
151 <button onClick={this.incrementCount}>increment</button>
152 </div>
153 );
154 }
155 }
156 const onRender = (...args) => {};
157 const Profiler = React.unstable_Profiler || React.Profiler;
158 apps.push(
159 <Feature
160 key="unstable_Profiler"
161 label="unstable_Profiler"
162 version="16.4+">
163 <Profiler id="count" onRender={onRender}>
164 <div>
165 <ProfilerChild />
166 </div>
167 </Profiler>
168 </Feature>
169 );
170 case 3:
171 // createContext()
172 const LocaleContext = React.createContext();
173 LocaleContext.displayName = 'LocaleContext';
174 const ThemeContext = React.createContext();
175 apps.push(
176 <Feature key="createContext" label="createContext" version="16.3+">
177 <ThemeContext.Provider value="blue">
178 <ThemeContext.Consumer>
179 {theme => <div>theme: {theme}</div>}
180 </ThemeContext.Consumer>
181 </ThemeContext.Provider>
182 <LocaleContext.Provider value="en-US">
183 <LocaleContext.Consumer>
184 {locale => <div>locale: {locale}</div>}
185 </LocaleContext.Consumer>
186 </LocaleContext.Provider>
187 </Feature>
188 );
189
190 // forwardRef()
191 const AnonymousFunction = React.forwardRef((props, ref) => (
192 <div ref={ref}>{props.children}</div>
193 ));
194 const NamedFunction = React.forwardRef(function named(props, ref) {
195 return <div ref={ref}>{props.children}</div>;
196 });
197 const CustomName = React.forwardRef((props, ref) => (
198 <div ref={ref}>{props.children}</div>
199 ));
200 CustomName.displayName = 'CustomNameForwardRef';
201 apps.push(
202 <Feature key="forwardRef" label="forwardRef" version="16.3+">
203 <AnonymousFunction>AnonymousFunction</AnonymousFunction>
204 <NamedFunction>NamedFunction</NamedFunction>
205 <CustomName>CustomName</CustomName>
206 </Feature>
207 );
208
209 // StrictMode
210 class StrictModeChild extends React.Component {
211 render() {
212 return 'StrictModeChild';
213 }
214 }
215 apps.push(
216 <Feature key="StrictMode" label="StrictMode" version="16.3+">
217 <React.StrictMode>
218 <StrictModeChild />
219 </React.StrictMode>
220 </Feature>
221 );
222
223 // unstable_AsyncMode (later renamed to unstable_ConcurrentMode, then ConcurrentMode)
224 const ConcurrentMode =
225 React.ConcurrentMode ||
226 React.unstable_ConcurrentMode ||
227 React.unstable_AsyncMode;
228 apps.push(
229 <Feature
230 key="AsyncMode/ConcurrentMode"
231 label="AsyncMode/ConcurrentMode"
232 version="16.3+">
233 <ConcurrentMode>
234 <div>
235 unstable_AsyncMode was added in 16.3, renamed to
236 unstable_ConcurrentMode in 16.5, and then renamed to
237 ConcurrentMode in 16.7
238 </div>
239 </ConcurrentMode>
240 </Feature>
241 );
242 case 2:
243 // Fragment
244 apps.push(
245 <Feature key="Fragment" label="Fragment" version="16.4+">
246 <React.Fragment>
247 <div>one</div>
248 <div>two</div>
249 </React.Fragment>
250 </Feature>
251 );
252 case 1:
253 case 0:
254 default:
255 break;
256 }
257 break;
258 case 15:
259 break;
260 case 14:
261 break;
262 default:
263 break;
264 }
265
266 function Even() {
267 return <small>(even)</small>;
268 }
269
270 // Simple stateful app shared by all React versions
271 class SimpleApp extends React.Component {
272 state = {count: 0};
273 incrementCount = () => {
274 const updaterFn = prevState => ({count: prevState.count + 1});
275 trace('Updating count', performance.now(), () => this.setState(updaterFn));
276 };
277 render() {
278 const {count} = this.state;
279 return (
280 <div>
281 {count % 2 === 0 ? (
282 <span>
283 count: {count} <Even />
284 </span>
285 ) : (
286 <span>count: {count}</span>
287 )}{' '}
288 <button onClick={this.incrementCount}>increment</button>
289 </div>
290 );
291 }
292 }
293 apps.push(
294 <Feature key="Simple stateful app" label="Simple stateful app" version="any">
295 <SimpleApp />
296 </Feature>
297 );
298
299 // This component, with the version prop, helps organize DevTools at a glance.
300 function TopLevelWrapperForDevTools({version}) {
301 let header = <h1>React {version}</h1>;
302 if (version.includes('canary')) {
303 const commitSha = version.match(/.+canary-(.+)/)[1];
304 header = (
305 <h1>
306 React canary{' '}
307 <a href={`https://github.com/facebook/react/commit/${commitSha}`}>
308 {commitSha}
309 </a>
310 </h1>
311 );
312 } else if (version.includes('alpha')) {
313 header = <h1>React next</h1>;
314 }
315
316 return (
317 <div>
318 {header}
319 {apps}
320 </div>
321 );
322 }
323 TopLevelWrapperForDevTools.displayName = 'React';
324
325 ReactDOM.render(
326 <TopLevelWrapperForDevTools version={React.version} />,
327 document.getElementById('root')
328 );