main
js 146 lines 3.59 KB
Raw
1 import React, {PureComponent, startTransition} from 'react';
2 import {createRoot} from 'react-dom/client';
3 import _ from 'lodash';
4 import Charts from './Charts';
5 import Clock from './Clock';
6 import './index.css';
7
8 let cachedData = new Map();
9
10 class App extends PureComponent {
11 state = {
12 value: '',
13 strategy: 'sync',
14 showDemo: true,
15 showClock: false,
16 };
17
18 // Random data for the chart
19 getStreamData(input) {
20 if (cachedData.has(input)) {
21 return cachedData.get(input);
22 }
23 const multiplier = input.length !== 0 ? input.length : 1;
24 const complexity =
25 (parseInt(window.location.search.slice(1), 10) / 100) * 25 || 25;
26 const data = _.range(5).map(t =>
27 _.range(complexity * multiplier).map((j, i) => {
28 return {
29 x: j,
30 y: (t + 1) * _.random(0, 255),
31 };
32 })
33 );
34 cachedData.set(input, data);
35 return data;
36 }
37
38 componentDidMount() {
39 window.addEventListener('keydown', e => {
40 if (e.key.toLowerCase() === '?') {
41 e.preventDefault();
42 this.setState(state => ({
43 showClock: !state.showClock,
44 }));
45 }
46 });
47 }
48
49 handleChartClick = e => {
50 if (this.state.showDemo) {
51 if (e.shiftKey) {
52 this.setState({showDemo: false});
53 }
54 return;
55 }
56 if (this.state.strategy !== 'async') {
57 this.setState(state => ({
58 showDemo: !state.showDemo,
59 }));
60 return;
61 }
62 if (this._ignoreClick) {
63 return;
64 }
65 this._ignoreClick = true;
66
67 startTransition(() => {
68 this.setState({showDemo: true}, () => {
69 this._ignoreClick = false;
70 });
71 });
72 };
73
74 debouncedHandleChange = _.debounce(value => {
75 if (this.state.strategy === 'debounced') {
76 this.setState({value: value});
77 }
78 }, 1000);
79
80 renderOption(strategy, label) {
81 const {strategy: currentStrategy} = this.state;
82 return (
83 <label className={strategy === currentStrategy ? 'selected' : null}>
84 <input
85 type="radio"
86 checked={strategy === currentStrategy}
87 onChange={() => this.setState({strategy})}
88 />
89 {label}
90 </label>
91 );
92 }
93
94 handleChange = e => {
95 const value = e.target.value;
96 const {strategy} = this.state;
97 switch (strategy) {
98 case 'sync':
99 this.setState({value});
100 break;
101 case 'debounced':
102 this.debouncedHandleChange(value);
103 break;
104 case 'async':
105 // TODO: useTransition hook instead.
106 startTransition(() => {
107 this.setState({value});
108 });
109 break;
110 default:
111 break;
112 }
113 };
114
115 render() {
116 const {showClock} = this.state;
117 const data = this.getStreamData(this.state.value);
118 return (
119 <div className="container">
120 <div className="rendering">
121 {this.renderOption('sync', 'Synchronous')}
122 {this.renderOption('debounced', 'Debounced')}
123 {this.renderOption('async', 'Concurrent')}
124 </div>
125 <input
126 className={'input ' + this.state.strategy}
127 placeholder="longer input → more components and DOM nodes"
128 defaultValue={this.state.input}
129 onChange={this.handleChange}
130 />
131 <div className="demo" onClick={this.handleChartClick}>
132 {this.state.showDemo && (
133 <Charts data={data} onClick={this.handleChartClick} />
134 )}
135 <div style={{display: showClock ? 'block' : 'none'}}>
136 <Clock />
137 </div>
138 </div>
139 </div>
140 );
141 }
142 }
143
144 const container = document.getElementById('root');
145 const root = createRoot(container);
146 root.render(<App />);