1- import React from ' react' ;
2- import ReactDOM from ' react-dom' ;
3- import { createStore } from ' redux' ;
4- import { Provider , connect } from ' react-redux' ;
1+ import React , { useCallback } from " react" ;
2+ import ReactDOM from " react-dom" ;
3+ import { createStore } from " redux" ;
4+ import { Provider , useDispatch , useSelector } from " react-redux" ;
55
6- function random ( max ) {
7- return Math . round ( Math . random ( ) * 1000 ) % max ;
8- }
9-
10- const A = [ "pretty" , "large" , "big" , "small" , "tall" , "short" , "long" , "handsome" , "plain" , "quaint" , "clean" , "elegant" , "easy" , "angry" , "crazy" , "helpful" , "mushy" , "odd" , "unsightly" , "adorable" , "important" , "inexpensive" , "cheap" , "expensive" , "fancy" ] ;
6+ const A = [ "pretty" , "large" , "big" , "small" , "tall" , "short" , "long" , "handsome" , "plain" , "quaint" , "clean" ,
7+ "elegant" , "easy" , "angry" , "crazy" , "helpful" , "mushy" , "odd" , "unsightly" , "adorable" , "important" , "inexpensive" ,
8+ "cheap" , "expensive" , "fancy" ] ;
119const C = [ "red" , "yellow" , "blue" , "green" , "pink" , "brown" , "purple" , "brown" , "white" , "black" , "orange" ] ;
12- const N = [ "table" , "chair" , "house" , "bbq" , "desk" , "car" , "pony" , "cookie" , "sandwich" , "burger" , "pizza" , "mouse" , "keyboard" ] ;
10+ const N = [ "table" , "chair" , "house" , "bbq" , "desk" , "car" , "pony" , "cookie" , "sandwich" , "burger" , "pizza" , "mouse" ,
11+ "keyboard" ] ;
1312
14- let nextId = 1 ;
13+ const random = ( max ) => Math . round ( Math . random ( ) * 1000 ) % max ;
1514
15+ let nextId = 1 ;
1616function buildData ( count ) {
1717 const data = new Array ( count ) ;
1818 for ( let i = 0 ; i < count ; i ++ ) {
@@ -27,126 +27,98 @@ function buildData(count) {
2727const store = createStore ( ( state = { data : [ ] , selected : 0 } , action ) => {
2828 const { data, selected } = state ;
2929 switch ( action . type ) {
30- case ' RUN' :
30+ case " RUN" :
3131 return { data : buildData ( 1000 ) , selected : 0 } ;
32- case ' RUN_LOTS' :
32+ case " RUN_LOTS" :
3333 return { data : buildData ( 10000 ) , selected : 0 } ;
34- case ' ADD' :
34+ case " ADD" :
3535 return { data : data . concat ( buildData ( 1000 ) ) , selected } ;
36- case ' UPDATE' :
36+ case " UPDATE" : {
3737 const newData = data . slice ( ) ;
3838 for ( let i = 0 ; i < newData . length ; i += 10 ) {
3939 const r = newData [ i ] ;
4040 newData [ i ] = { id : r . id , label : r . label + " !!!" } ;
4141 }
4242 return { data : newData , selected } ;
43- case 'REMOVE' :
44- const idx = data . findIndex ( ( d ) => d . id === action . id ) ;
45- return { data : [ ...data . slice ( 0 , idx ) , ...data . slice ( idx + 1 ) ] , selected } ;
46- case 'SELECT' :
43+ }
44+ case "REMOVE" : {
45+ const newData = data . slice ( ) ;
46+ newData . splice ( data . indexOf ( action . item ) , 1 ) ;
47+ return { data : newData , selected } ;
48+ }
49+ case "SELECT" :
4750 return { data, selected : action . id } ;
48- case ' CLEAR' :
51+ case " CLEAR" :
4952 return { data : [ ] , selected : 0 } ;
50- case 'SWAP_ROWS' :
51- return { data : [ data [ 0 ] , data [ 998 ] , ...data . slice ( 2 , 998 ) , data [ 1 ] , data [ 999 ] ] , selected } ;
53+ case "SWAP_ROWS" : {
54+ const newData = data . slice ( ) ;
55+ const tmp = newData [ 1 ] ;
56+ newData [ 1 ] = newData [ 998 ] ;
57+ newData [ 998 ] = tmp ;
58+ return { data : newData , selected } ;
59+ }
5260 }
5361 return state ;
5462} ) ;
5563
56- function select ( id ) {
57- return { type : 'SELECT' , id } ;
58- }
59-
60- function remove ( id ) {
61- return { type : 'REMOVE' , id } ;
62- }
63-
6464const GlyphIcon = < span className = "glyphicon glyphicon-remove" aria-hidden = "true" > </ span > ;
6565
66- const Row = connect (
67- ( state , { i } ) => {
68- const item = state . data [ i ] ;
69- return state . selected === item . id ? { id : item . id , label : item . label , selected : true } : item ;
70- } ,
71- { select , remove } ,
72- ) ( class extends React . Component {
73- onSelect = ( ) => {
74- this . props . select ( this . props . id ) ;
75- } ;
76-
77- onRemove = ( ) => {
78- this . props . remove ( this . props . id ) ;
79- } ;
66+ const Row = React . memo ( ( { data } ) => {
67+ const isSelected = useSelector ( ( state ) => state . selected === data . id , [ data ] ) ;
68+ const dispatch = useDispatch ( ) ;
69+ const select = useCallback ( ( ) => { dispatch ( { type : "SELECT" , id : data . id } ) ; } , [ data ] ) ;
70+ const remove = useCallback ( ( ) => { dispatch ( { type : "REMOVE" , item : data } ) ; } , [ data ] ) ;
71+ return (
72+ < tr className = { isSelected ? "danger" : "" } >
73+ < td className = "col-md-1" > { data . id } </ td >
74+ < td className = "col-md-4" > < a onClick = { select } > { data . label } </ a > </ td >
75+ < td className = "col-md-1" > < a onClick = { remove } > { GlyphIcon } </ a > </ td >
76+ < td className = "col-md-6" > </ td >
77+ </ tr >
78+ )
79+ } ) ;
8080
81- render ( ) {
82- const { selected, id, label } = this . props ;
83- return (
84- < tr className = { selected ? "danger" : "" } >
85- < td className = "col-md-1" > { id } </ td >
86- < td className = "col-md-4" > < a onClick = { this . onSelect } > { label } </ a > </ td >
87- < td className = "col-md-1" > < a onClick = { this . onRemove } > { GlyphIcon } </ a > </ td >
88- < td className = "col-md-6" > </ td >
89- </ tr >
90- ) ;
91- }
81+ const RowList = React . memo ( ( ) => {
82+ const rows = useSelector ( ( state ) => state . data ) ;
83+ return rows . map ( ( data ) => < Row key = { data . id } data = { data } /> ) ;
9284} ) ;
9385
94- const RowList = connect (
95- ( state ) => ( { data : state . data } ) ,
96- null ,
97- null ,
98- {
99- areStatesEqual : ( prev , next ) => prev . data === next . data ,
100- } ,
101- ) ( ( { data } ) => data . map ( ( item , i ) => < Row key = { item . id } i = { i } /> ) ) ;
86+ const Button = React . memo ( ( { id, title, cb } ) => (
87+ < div className = "col-sm-6 smallpad" >
88+ < button type = "button" className = "btn btn-primary btn-block" id = { id } onClick = { cb } > { title } </ button >
89+ </ div >
90+ ) ) ;
10291
103- function Button ( props ) {
92+ const Main = ( ) => {
93+ const dispatch = useDispatch ( ) ;
94+ const run = useCallback ( ( ) => { dispatch ( { type : "RUN" } ) ; } , [ ] ) ;
95+ const runLots = useCallback ( ( ) => { dispatch ( { type : "RUN_LOTS" } ) ; } , [ ] ) ;
96+ const add = useCallback ( ( ) => { dispatch ( { type : "ADD" } ) ; } , [ ] ) ;
97+ const update = useCallback ( ( ) => { dispatch ( { type : "UPDATE" } ) ; } , [ ] ) ;
98+ const clear = useCallback ( ( ) => { dispatch ( { type : "CLEAR" } ) ; } , [ ] ) ;
99+ const swapRows = useCallback ( ( ) => { dispatch ( { type : "SWAP_ROWS" } ) ; } , [ ] ) ;
104100 return (
105- < div className = "col-sm-6 smallpad" >
106- < button type = "button" className = "btn btn-primary btn-block" id = { props . id } onClick = { props . cb } > { props . title } </ button >
107- </ div >
108- ) ;
109- }
110-
111- const Main = connect (
112- null ,
113- {
114- run : ( ) => ( { type : 'RUN' } ) ,
115- runLots : ( ) => ( { type : 'RUN_LOTS' } ) ,
116- add : ( ) => ( { type : 'ADD' } ) ,
117- update : ( ) => ( { type : 'UPDATE' } ) ,
118- clear : ( ) => ( { type : 'CLEAR' } ) ,
119- swapRows : ( ) => ( { type : 'SWAP_ROWS' } ) ,
120- } ,
121- ) ( ( props ) => (
122- < div className = "container" >
123- < div className = "jumbotron" >
124- < div className = "row" >
125- < div className = "col-md-6" >
126- < h1 > React + Redux</ h1 >
127- </ div >
128- < div className = "col-md-6" >
129- < div className = "row" >
130- < Button id = "run" title = "Create 1,000 rows" cb = { props . run } />
131- < Button id = "runlots" title = "Create 10,000 rows" cb = { props . runLots } />
132- < Button id = "add" title = "Append 1,000 rows" cb = { props . add } />
133- < Button id = "update" title = "Update every 10th row" cb = { props . update } />
134- < Button id = "clear" title = "Clear" cb = { props . clear } />
135- < Button id = "swaprows" title = "Swap Rows" cb = { props . swapRows } />
136- </ div >
101+ < div className = "container" >
102+ < div className = "jumbotron" >
103+ < div className = "row" >
104+ < div className = "col-md-6" > < h1 > React + Redux</ h1 > </ div >
105+ < div className = "col-md-6" > < div className = "row" >
106+ < Button id = "run" title = "Create 1,000 rows" cb = { run } />
107+ < Button id = "runlots" title = "Create 10,000 rows" cb = { runLots } />
108+ < Button id = "add" title = "Append 1,000 rows" cb = { add } />
109+ < Button id = "update" title = "Update every 10th row" cb = { update } />
110+ < Button id = "clear" title = "Clear" cb = { clear } />
111+ < Button id = "swaprows" title = "Swap Rows" cb = { swapRows } />
112+ </ div > </ div >
137113 </ div >
138114 </ div >
115+ < table className = "table table-hover table-striped test-data" > < tbody > < RowList /> </ tbody > </ table >
116+ < span className = "preloadicon glyphicon glyphicon-remove" aria-hidden = "true" > </ span >
139117 </ div >
140- < table className = "table table-hover table-striped test-data" > < tbody > < RowList /> </ tbody > </ table >
141- < span className = "preloadicon glyphicon glyphicon-remove" aria-hidden = "true" > </ span >
142- </ div >
143- ) ) ;
118+ ) ;
119+ } ;
144120
145121ReactDOM . render (
146- (
147- < Provider store = { store } >
148- < Main />
149- </ Provider >
150- ) ,
122+ < Provider store = { store } > < Main /> </ Provider > ,
151123 document . getElementById ( "main" )
152124) ;
0 commit comments