fixtures/devtools/regression/shared.js (300 lines of code) (raw):

/* eslint-disable no-fallthrough, react/react-in-jsx-scope, react/jsx-no-undef */ /* global React ReactCache ReactDOM SchedulerTracing ScheduleTracing */ const apps = []; const pieces = React.version.split('.'); const major = pieces[0] === '0' ? parseInt(pieces[1], 10) : parseInt(pieces[0], 10); const minor = pieces[0] === '0' ? parseInt(pieces[2], 10) : parseInt(pieces[1], 10); // Convenience wrapper to organize API features in DevTools. function Feature({children, label, version}) { return ( <div className="Feature"> <div className="FeatureHeader"> <code className="FeatureCode">{label}</code> <small>{version}</small> </div> {children} </div> ); } // Simplify interaction tracing for tests below. let trace = null; if (typeof SchedulerTracing !== 'undefined') { trace = SchedulerTracing.unstable_trace; } else if (typeof ScheduleTracing !== 'undefined') { trace = ScheduleTracing.unstable_trace; } else { trace = (_, __, callback) => callback(); } // https://github.com/facebook/react/blob/main/CHANGELOG.md switch (major) { case 16: switch (minor) { case 7: if (typeof React.useState === 'function') { // Hooks function Hooks() { const [count, setCount] = React.useState(0); const incrementCount = React.useCallback( () => setCount(count + 1), [count] ); return ( <div> count: {count}{' '} <button onClick={incrementCount}>increment</button> </div> ); } apps.push( <Feature key="Hooks" label="Hooks" version="16.7+"> <Hooks /> </Feature> ); } case 6: // memo function LabelComponent({label}) { return <label>{label}</label>; } const AnonymousMemoized = React.memo(({label}) => ( <label>{label}</label> )); const Memoized = React.memo(LabelComponent); const CustomMemoized = React.memo(LabelComponent); CustomMemoized.displayName = 'MemoizedLabelFunction'; apps.push( <Feature key="memo" label="memo" version="16.6+"> <AnonymousMemoized label="AnonymousMemoized" /> <Memoized label="Memoized" /> <CustomMemoized label="CustomMemoized" /> </Feature> ); // Suspense const loadResource = ([text, ms]) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(text); }, ms); }); }; const getResourceKey = ([text, ms]) => text; const Resource = ReactCache.unstable_createResource( loadResource, getResourceKey ); class Suspending extends React.Component { state = {useSuspense: false}; useSuspense = () => this.setState({useSuspense: true}); render() { if (this.state.useSuspense) { const text = Resource.read(['loaded', 2000]); return text; } else { return <button onClick={this.useSuspense}>load data</button>; } } } apps.push( <Feature key="Suspense" label="Suspense" version="16.6+"> <React.Suspense fallback={<div>loading...</div>}> <Suspending /> </React.Suspense> </Feature> ); // lazy const LazyWithDefaultProps = React.lazy( () => new Promise(resolve => { function FooWithDefaultProps(props) { return ( <h1> {props.greeting}, {props.name} </h1> ); } FooWithDefaultProps.defaultProps = { name: 'World', greeting: 'Bonjour', }; resolve({ default: FooWithDefaultProps, }); }) ); apps.push( <Feature key="lazy" label="lazy" version="16.6+"> <React.Suspense fallback={<div>loading...</div>}> <LazyWithDefaultProps greeting="Hello" /> </React.Suspense> </Feature> ); case 5: case 4: // unstable_Profiler class ProfilerChild extends React.Component { state = {count: 0}; incrementCount = () => this.setState(prevState => ({count: prevState.count + 1})); render() { return ( <div> count: {this.state.count}{' '} <button onClick={this.incrementCount}>increment</button> </div> ); } } const onRender = (...args) => {}; const Profiler = React.unstable_Profiler || React.Profiler; apps.push( <Feature key="unstable_Profiler" label="unstable_Profiler" version="16.4+"> <Profiler id="count" onRender={onRender}> <div> <ProfilerChild /> </div> </Profiler> </Feature> ); case 3: // createContext() const LocaleContext = React.createContext(); LocaleContext.displayName = 'LocaleContext'; const ThemeContext = React.createContext(); apps.push( <Feature key="createContext" label="createContext" version="16.3+"> <ThemeContext.Provider value="blue"> <ThemeContext.Consumer> {theme => <div>theme: {theme}</div>} </ThemeContext.Consumer> </ThemeContext.Provider> <LocaleContext.Provider value="en-US"> <LocaleContext.Consumer> {locale => <div>locale: {locale}</div>} </LocaleContext.Consumer> </LocaleContext.Provider> </Feature> ); // forwardRef() const AnonymousFunction = React.forwardRef((props, ref) => ( <div ref={ref}>{props.children}</div> )); const NamedFunction = React.forwardRef(function named(props, ref) { return <div ref={ref}>{props.children}</div>; }); const CustomName = React.forwardRef((props, ref) => ( <div ref={ref}>{props.children}</div> )); CustomName.displayName = 'CustomNameForwardRef'; apps.push( <Feature key="forwardRef" label="forwardRef" version="16.3+"> <AnonymousFunction>AnonymousFunction</AnonymousFunction> <NamedFunction>NamedFunction</NamedFunction> <CustomName>CustomName</CustomName> </Feature> ); // StrictMode class StrictModeChild extends React.Component { render() { return 'StrictModeChild'; } } apps.push( <Feature key="StrictMode" label="StrictMode" version="16.3+"> <React.StrictMode> <StrictModeChild /> </React.StrictMode> </Feature> ); // unstable_AsyncMode (later renamed to unstable_ConcurrentMode, then ConcurrentMode) const ConcurrentMode = React.ConcurrentMode || React.unstable_ConcurrentMode || React.unstable_AsyncMode; apps.push( <Feature key="AsyncMode/ConcurrentMode" label="AsyncMode/ConcurrentMode" version="16.3+"> <ConcurrentMode> <div> unstable_AsyncMode was added in 16.3, renamed to unstable_ConcurrentMode in 16.5, and then renamed to ConcurrentMode in 16.7 </div> </ConcurrentMode> </Feature> ); case 2: // Fragment apps.push( <Feature key="Fragment" label="Fragment" version="16.4+"> <React.Fragment> <div>one</div> <div>two</div> </React.Fragment> </Feature> ); case 1: case 0: default: break; } break; case 15: break; case 14: break; default: break; } function Even() { return <small>(even)</small>; } // Simple stateful app shared by all React versions class SimpleApp extends React.Component { state = {count: 0}; incrementCount = () => { const updaterFn = prevState => ({count: prevState.count + 1}); trace('Updating count', performance.now(), () => this.setState(updaterFn)); }; render() { const {count} = this.state; return ( <div> {count % 2 === 0 ? ( <span> count: {count} <Even /> </span> ) : ( <span>count: {count}</span> )}{' '} <button onClick={this.incrementCount}>increment</button> </div> ); } } apps.push( <Feature key="Simple stateful app" label="Simple stateful app" version="any"> <SimpleApp /> </Feature> ); // This component, with the version prop, helps organize DevTools at a glance. function TopLevelWrapperForDevTools({version}) { let header = <h1>React {version}</h1>; if (version.includes('canary')) { const commitSha = version.match(/.+canary-(.+)/)[1]; header = ( <h1> React canary{' '} <a href={`https://github.com/facebook/react/commit/${commitSha}`}> {commitSha} </a> </h1> ); } else if (version.includes('alpha')) { header = <h1>React next</h1>; } return ( <div> {header} {apps} </div> ); } TopLevelWrapperForDevTools.displayName = 'React'; ReactDOM.render( <TopLevelWrapperForDevTools version={React.version} />, document.getElementById('root') );