1
- import React , { Component } from "react" ;
2
- import { createRoot } from "react-dom" ;
1
+ import React from 'react' ;
2
+ import { memo , useReducer } from 'react' ;
3
+ import { createRoot } from 'react-dom' ;
3
4
4
5
const random = ( max ) => Math . round ( Math . random ( ) * 1000 ) % max ;
5
6
6
- const A = [
7
- "pretty" ,
8
- "large" ,
9
- "big" ,
10
- "small" ,
11
- "tall" ,
12
- "short" ,
13
- "long" ,
14
- "handsome" ,
15
- "plain" ,
16
- "quaint" ,
17
- "clean" ,
18
- "elegant" ,
19
- "easy" ,
20
- "angry" ,
21
- "crazy" ,
22
- "helpful" ,
23
- "mushy" ,
24
- "odd" ,
25
- "unsightly" ,
26
- "adorable" ,
27
- "important" ,
28
- "inexpensive" ,
29
- "cheap" ,
30
- "expensive" ,
31
- "fancy" ,
32
- ] ;
33
- const C = [
34
- "red" ,
35
- "yellow" ,
36
- "blue" ,
37
- "green" ,
38
- "pink" ,
39
- "brown" ,
40
- "purple" ,
41
- "brown" ,
42
- "white" ,
43
- "black" ,
44
- "orange" ,
45
- ] ;
46
- const N = [
47
- "table" ,
48
- "chair" ,
49
- "house" ,
50
- "bbq" ,
51
- "desk" ,
52
- "car" ,
53
- "pony" ,
54
- "cookie" ,
55
- "sandwich" ,
56
- "burger" ,
57
- "pizza" ,
58
- "mouse" ,
59
- "keyboard" ,
60
- ] ;
7
+ const A = [ "pretty" , "large" , "big" , "small" , "tall" , "short" , "long" , "handsome" , "plain" , "quaint" , "clean" ,
8
+ "elegant" , "easy" , "angry" , "crazy" , "helpful" , "mushy" , "odd" , "unsightly" , "adorable" , "important" , "inexpensive" ,
9
+ "cheap" , "expensive" , "fancy" ] ;
10
+ const C = [ "red" , "yellow" , "blue" , "green" , "pink" , "brown" , "purple" , "brown" , "white" , "black" , "orange" ] ;
11
+ const N = [ "table" , "chair" , "house" , "bbq" , "desk" , "car" , "pony" , "cookie" , "sandwich" , "burger" , "pizza" , "mouse" ,
12
+ "keyboard" ] ;
61
13
62
14
let nextId = 1 ;
63
15
@@ -67,9 +19,7 @@ const buildData = (count) => {
67
19
for ( let i = 0 ; i < count ; i ++ ) {
68
20
data [ i ] = {
69
21
id : nextId ++ ,
70
- label : `${ A [ random ( A . length ) ] } ${ C [ random ( C . length ) ] } ${
71
- N [ random ( N . length ) ]
72
- } `,
22
+ label : `${ A [ random ( A . length ) ] } ${ C [ random ( C . length ) ] } ${ N [ random ( N . length ) ] } ` ,
73
23
} ;
74
24
}
75
25
@@ -78,204 +28,98 @@ const buildData = (count) => {
78
28
79
29
const initialState = { data : [ ] , selected : 0 } ;
80
30
81
- class Row extends Component {
82
- shouldComponentUpdate ( nextProps ) {
83
- const { item, selected } = this . props ;
31
+ const listReducer = ( state , action ) => {
32
+ const { data, selected } = state ;
84
33
85
- return nextProps . item !== item || nextProps . selected !== selected ;
86
- }
87
-
88
- constructor ( props ) {
89
- super ( props ) ;
90
-
91
- this . onSelect = ( ) => {
92
- const { item, dispatch } = this . props ;
93
-
94
- dispatch ( { type : "SELECT" , id : item . id } ) ;
95
- } ;
96
-
97
- this . onRemove = ( ) => {
98
- const { item, dispatch } = this . props ;
99
-
100
- dispatch ( { type : "REMOVE" , id : item . id } ) ;
101
- } ;
102
- }
103
-
104
- render ( ) {
105
- const { selected, item } = this . props ;
34
+ switch ( action . type ) {
35
+ case 'RUN' :
36
+ return { data : buildData ( 1000 ) , selected : 0 } ;
37
+ case 'RUN_LOTS' :
38
+ return { data : buildData ( 10000 ) , selected : 0 } ;
39
+ case 'ADD' :
40
+ return { data : data . concat ( buildData ( 1000 ) ) , selected } ;
41
+ case 'UPDATE' : {
42
+ const newData = data . slice ( 0 ) ;
106
43
107
- return (
108
- < tr className = { selected ? "danger" : "" } >
109
- < td className = "col-md-1" > { item . id } </ td >
110
- < td className = "col-md-4" >
111
- < a onClick = { this . onSelect } > { item . label } </ a >
112
- </ td >
113
- < td className = "col-md-1" >
114
- < a onClick = { this . onRemove } >
115
- < span className = "glyphicon glyphicon-remove" aria-hidden = "true" />
116
- </ a >
117
- </ td >
118
- < td className = "col-md-6" />
119
- </ tr >
120
- ) ;
121
- }
122
- }
123
-
124
- class Button extends Component {
125
- render ( ) {
126
- const { id, cb, title } = this . props ;
44
+ for ( let i = 0 ; i < newData . length ; i += 10 ) {
45
+ const r = newData [ i ] ;
127
46
128
- return (
129
- < div className = "col-sm-6 smallpad" >
130
- < button
131
- type = "button"
132
- className = "btn btn-primary btn-block"
133
- id = { id }
134
- onClick = { cb }
135
- >
136
- { title }
137
- </ button >
138
- </ div >
139
- ) ;
140
- }
141
- }
47
+ newData [ i ] = { id : r . id , label : r . label + " !!!" } ;
48
+ }
142
49
143
- class Jumbotron extends Component {
144
- shouldComponentUpdate ( ) {
145
- return false ;
50
+ return { data : newData , selected } ;
51
+ }
52
+ case 'CLEAR' :
53
+ return { data : [ ] , selected : 0 } ;
54
+ case 'SWAP_ROWS' :
55
+ return data . length > 998 ? { data : [ data [ 0 ] , data [ 998 ] , ...data . slice ( 2 , 998 ) , data [ 1 ] , data [ 999 ] ] , selected } : state ;
56
+ case 'REMOVE' : {
57
+ const idx = data . findIndex ( ( d ) => d . id === action . id ) ;
58
+
59
+ return { data : [ ...data . slice ( 0 , idx ) , ...data . slice ( idx + 1 ) ] , selected } ;
60
+ }
61
+ case 'SELECT' :
62
+ return { data, selected : action . id } ;
63
+ default :
64
+ return state ;
146
65
}
66
+ } ;
147
67
148
- render ( ) {
149
- const { dispatch } = this . props ;
150
-
151
- return (
152
- < div className = "jumbotron" >
153
- < div className = "row" >
154
- < div className = "col-md-6" >
155
- < h1 > React Lab keyed</ h1 >
156
- </ div >
157
- < div className = "col-md-6" >
158
- < div className = "row" >
159
- < Button
160
- id = "run"
161
- title = "Create 1,000 rows"
162
- cb = { ( ) => dispatch ( { type : "RUN" } ) }
163
- />
164
- < Button
165
- id = "runlots"
166
- title = "Create 10,000 rows"
167
- cb = { ( ) => dispatch ( { type : "RUN_LOTS" } ) }
168
- />
169
- < Button
170
- id = "add"
171
- title = "Append 1,000 rows"
172
- cb = { ( ) => dispatch ( { type : "ADD" } ) }
173
- />
174
- < Button
175
- id = "update"
176
- title = "Update every 10th row"
177
- cb = { ( ) => dispatch ( { type : "UPDATE" } ) }
178
- />
179
- < Button
180
- id = "clear"
181
- title = "Clear"
182
- cb = { ( ) => dispatch ( { type : "CLEAR" } ) }
183
- />
184
- < Button
185
- id = "swaprows"
186
- title = "Swap Rows"
187
- cb = { ( ) => dispatch ( { type : "SWAP_ROWS" } ) }
188
- />
189
- </ div >
68
+ const Row = memo ( ( { selected, item, dispatch } ) => (
69
+ < tr className = { selected ? "danger" : "" } >
70
+ < td className = "col-md-1" > { item . id } </ td >
71
+ < td className = "col-md-4" >
72
+ < a onClick = { ( ) => dispatch ( { type : 'SELECT' , id : item . id } ) } > { item . label } </ a >
73
+ </ td >
74
+ < td className = "col-md-1" >
75
+ < a onClick = { ( ) => dispatch ( { type : 'REMOVE' , id : item . id } ) } >
76
+ < span className = "glyphicon glyphicon-remove" aria-hidden = "true" />
77
+ </ a >
78
+ </ td >
79
+ < td className = "col-md-6" />
80
+ </ tr >
81
+ ) , ( prevProps , nextProps ) => prevProps . selected === nextProps . selected && prevProps . item === nextProps . item )
82
+
83
+ const Button = ( { id, cb, title } ) => (
84
+ < div className = "col-sm-6 smallpad" >
85
+ < button type = "button" className = "btn btn-primary btn-block" id = { id } onClick = { cb } > { title } </ button >
86
+ </ div >
87
+ ) ;
88
+
89
+ const Jumbotron = memo ( ( { dispatch } ) => (
90
+ < div className = "jumbotron" >
91
+ < div className = "row" >
92
+ < div className = "col-md-6" >
93
+ < h1 > React Lab Hooks keyed</ h1 >
94
+ </ div >
95
+ < div className = "col-md-6" >
96
+ < div className = "row" >
97
+ < Button id = "run" title = "Create 1,000 rows" cb = { ( ) => dispatch ( { type : 'RUN' } ) } />
98
+ < Button id = "runlots" title = "Create 10,000 rows" cb = { ( ) => dispatch ( { type : 'RUN_LOTS' } ) } />
99
+ < Button id = "add" title = "Append 1,000 rows" cb = { ( ) => dispatch ( { type : 'ADD' } ) } />
100
+ < Button id = "update" title = "Update every 10th row" cb = { ( ) => dispatch ( { type : 'UPDATE' } ) } />
101
+ < Button id = "clear" title = "Clear" cb = { ( ) => dispatch ( { type : 'CLEAR' } ) } />
102
+ < Button id = "swaprows" title = "Swap Rows" cb = { ( ) => dispatch ( { type : 'SWAP_ROWS' } ) } />
190
103
</ div >
191
104
</ div >
192
105
</ div >
193
- ) ;
194
- }
195
- }
196
-
197
- class Main extends Component {
198
- constructor ( props ) {
199
- super ( props ) ;
200
-
201
- this . state = initialState ;
202
-
203
- this . dispatch = ( action ) => {
204
- const { data } = this . state ;
205
-
206
- switch ( action . type ) {
207
- case "RUN" :
208
- return this . setState ( { data : buildData ( 1000 ) , selected : 0 } ) ;
209
- case "RUN_LOTS" :
210
- return this . setState ( { data : buildData ( 10000 ) , selected : 0 } ) ;
211
- case "ADD" :
212
- return this . setState ( { data : data . concat ( buildData ( 1000 ) ) } ) ;
213
- case "UPDATE" : {
214
- const newData = data . slice ( 0 ) ;
215
-
216
- for ( let i = 0 ; i < newData . length ; i += 10 ) {
217
- const r = newData [ i ] ;
218
-
219
- newData [ i ] = { id : r . id , label : r . label + " !!!" } ;
220
- }
221
-
222
- return this . setState ( { data : newData } ) ;
223
- }
224
- case "CLEAR" :
225
- return this . setState ( { data : [ ] , selected : 0 } ) ;
226
- case "SWAP_ROWS" : {
227
- if ( data . length > 998 ) {
228
- return this . setState ( {
229
- data : [
230
- data [ 0 ] ,
231
- data [ 998 ] ,
232
- ...data . slice ( 2 , 998 ) ,
233
- data [ 1 ] ,
234
- data [ 999 ] ,
235
- ] ,
236
- } ) ;
237
- }
238
-
239
- return ;
240
- }
241
- case "REMOVE" : {
242
- const idx = data . findIndex ( ( d ) => d . id === action . id ) ;
243
-
244
- return this . setState ( {
245
- data : [ ...data . slice ( 0 , idx ) , ...data . slice ( idx + 1 ) ] ,
246
- } ) ;
247
- }
248
- case "SELECT" :
249
- return this . setState ( { selected : action . id } ) ;
250
- }
251
- } ;
252
- }
253
-
254
- render ( ) {
255
- const { data, selected } = this . state ;
256
-
257
- return (
258
- < div className = "container" >
259
- < Jumbotron dispatch = { this . dispatch } />
260
- < table className = "table table-hover table-striped test-data" >
261
- < tbody >
262
- { data . map ( ( item ) => (
263
- < Row
264
- key = { item . id }
265
- item = { item }
266
- selected = { selected === item . id }
267
- dispatch = { this . dispatch }
268
- />
269
- ) ) }
270
- </ tbody >
271
- </ table >
272
- < span
273
- className = "preloadicon glyphicon glyphicon-remove"
274
- aria-hidden = "true"
275
- />
276
- </ div >
277
- ) ;
278
- }
106
+ </ div >
107
+ ) , ( ) => true ) ;
108
+
109
+ const Main = ( ) => {
110
+ const [ { data, selected } , dispatch ] = useReducer ( listReducer , initialState ) ;
111
+
112
+ return ( < div className = "container" >
113
+ < Jumbotron dispatch = { dispatch } />
114
+ < table className = "table table-hover table-striped test-data" >
115
+ < tbody >
116
+ { data . map ( item => (
117
+ < Row key = { item . id } item = { item } selected = { selected === item . id } dispatch = { dispatch } />
118
+ ) ) }
119
+ </ tbody >
120
+ </ table >
121
+ < span className = "preloadicon glyphicon glyphicon-remove" aria-hidden = "true" />
122
+ </ div > ) ;
279
123
}
280
124
281
- createRoot ( document . getElementById ( "main" ) ) . render ( < Main /> ) ;
125
+ createRoot ( document . getElementById ( "main" ) ) . render ( < Main /> ) ;
0 commit comments