1
1
import React from 'react' ;
2
2
import ReactDOM from 'react-dom' ;
3
- import { applyMiddleware , combineReducers , createStore } from 'redux' ;
4
- import { Provider , connect } from 'react-redux' ;
5
- import { createLogger } from 'redux-logger' ;
6
- import createSagaMiddleware , { delay } from 'redux-saga' ;
7
- import { put , takeEvery } from 'redux-saga/effects' ;
8
- import { schema , normalize } from 'normalizr' ;
9
- import uuid from 'uuid/v4' ;
3
+ import { combineReducers , createStore } from 'redux' ;
10
4
import './index.css' ;
11
5
12
- // filters
13
-
14
- const VISIBILITY_FILTERS = {
15
- SHOW_COMPLETED : item => item . completed ,
16
- SHOW_INCOMPLETED : item => ! item . completed ,
17
- SHOW_ALL : item => true ,
18
- } ;
19
-
20
- // schemas
21
-
22
- const todoSchema = new schema . Entity ( 'todo' ) ;
23
-
24
6
// action types
25
7
26
8
const TODO_ADD = 'TODO_ADD' ;
27
9
const TODO_TOGGLE = 'TODO_TOGGLE' ;
28
10
const FILTER_SET = 'FILTER_SET' ;
29
- const NOTIFICATION_HIDE = 'NOTIFICATION_HIDE' ;
30
- const TODO_ADD_WITH_NOTIFICATION = 'TODO_ADD_WITH_NOTIFICATION' ;
31
11
32
12
// reducers
33
13
34
14
const todos = [
35
- { id : uuid ( ) , name : 'Hands On: Snake with Local State' } ,
36
- { id : uuid ( ) , name : 'Challenge: Snake with Higher Order Components' } ,
37
- { id : uuid ( ) , name : 'Hands On: Redux Standalone with advanced Actions' } ,
38
- { id : uuid ( ) , name : 'Hands On: Redux Standalone with advanced Reducers' } ,
39
- { id : uuid ( ) , name : 'Hands On: Bootstrap App with Redux' } ,
40
- { id : uuid ( ) , name : 'Hands On: Naive Todo with React and Redux' } ,
41
- { id : uuid ( ) , name : 'Hands On: Sophisticated Todo with React and Redux' } ,
42
- { id : uuid ( ) , name : 'Hands On: Connecting State Everywhere' } ,
43
- { id : uuid ( ) , name : 'Challenge: Snake with React and Redux' } ,
44
- { id : uuid ( ) , name : 'Hands On: Todo with advanced Redux' } ,
45
- { id : uuid ( ) , name : 'Hands On: Todo with more Features' } ,
46
- { id : uuid ( ) , name : 'Challenge: Snake with Redux' } ,
47
- { id : uuid ( ) , name : 'Hands On: Todo with Notifications' } ,
48
- { id : uuid ( ) , name : 'Challenge: Snake with Redux and Async Actions' } ,
49
- { id : uuid ( ) , name : 'Hands On: Hacker News with Redux' } ,
50
- { id : uuid ( ) , name : 'Challenge: Hacker News with beyond Redux' } ,
51
- { id : uuid ( ) , name : 'Challenge: Hacker News with beyond Redux' } ,
52
- { id : uuid ( ) , name : 'Hands On: Snake with MobX' } ,
53
- { id : uuid ( ) , name : 'Hands On: Todo App with MobX' } ,
54
- { id : uuid ( ) , name : 'Challenge: Hacker News App with MobX' } ,
55
- { id : uuid ( ) , name : 'Challenge: Consuming a GrapQL API with Relay' } ,
15
+ { id : '0' , name : 'learn redux' } ,
16
+ { id : '1' , name : 'learn mobx' } ,
56
17
] ;
57
18
58
- const normalizedTodos = normalize ( todos , [ todoSchema ] ) ;
59
-
60
- const initialTodoState = {
61
- entities : normalizedTodos . entities . todo ,
62
- ids : normalizedTodos . result ,
63
- } ;
64
-
65
- function todoReducer ( state = initialTodoState , action ) {
19
+ function todoReducer ( state = todos , action ) {
66
20
switch ( action . type ) {
67
21
case TODO_ADD : {
68
22
return applyAddTodo ( state , action ) ;
@@ -75,18 +29,16 @@ function todoReducer(state = initialTodoState, action) {
75
29
}
76
30
77
31
function applyAddTodo ( state , action ) {
78
- const todo = { ...action . todo , completed : false } ;
79
- const entities = { ...state . entities , [ todo . id ] : todo } ;
80
- const ids = [ ...state . ids , action . todo . id ] ;
81
- return { ...state , entities, ids } ;
32
+ const todo = Object . assign ( { } , action . todo , { completed : false } ) ;
33
+ return state . concat ( todo ) ;
82
34
}
83
35
84
36
function applyToggleTodo ( state , action ) {
85
- const id = action . todo . id ;
86
- const todo = state . entities [ id ] ;
87
- const toggledTodo = { ... todo , completed : ! todo . completed } ;
88
- const entities = { ... state . entities , [ id ] : toggledTodo } ;
89
- return { ... state , entities } ;
37
+ return state . map ( todo =>
38
+ todo . id === action . todo . id
39
+ ? Object . assign ( { } , todo , { completed : ! todo . completed } )
40
+ : todo
41
+ ) ;
90
42
}
91
43
92
44
function filterReducer ( state = 'SHOW_ALL' , action ) {
@@ -102,47 +54,8 @@ function applySetFilter(state, action) {
102
54
return action . filter ;
103
55
}
104
56
105
- function notificationReducer ( state = { } , action ) {
106
- switch ( action . type ) {
107
- case TODO_ADD : {
108
- return applySetNotifyAboutAddTodo ( state , action ) ;
109
- }
110
- case NOTIFICATION_HIDE : {
111
- return applyRemoveNotification ( state , action ) ;
112
- }
113
- default : return state ;
114
- }
115
- }
116
-
117
- function applySetNotifyAboutAddTodo ( state , action ) {
118
- const { name, id } = action . todo ;
119
- return { ...state , [ id ] : 'Todo Created: ' + name } ;
120
- }
121
-
122
- function applyRemoveNotification ( state , action ) {
123
- const {
124
- [ action . id ] : notificationToRemove ,
125
- ...restNotifications ,
126
- } = state ;
127
- return restNotifications ;
128
- }
129
-
130
57
// action creators
131
58
132
- function doAddTodoWithNotification ( id , name ) {
133
- return {
134
- type : TODO_ADD_WITH_NOTIFICATION ,
135
- todo : { id, name } ,
136
- } ;
137
- }
138
-
139
- function doHideNotification ( id ) {
140
- return {
141
- type : NOTIFICATION_HIDE ,
142
- id
143
- } ;
144
- }
145
-
146
59
function doAddTodo ( id , name ) {
147
60
return {
148
61
type : TODO_ADD ,
@@ -164,213 +77,19 @@ function doSetFilter(filter) {
164
77
} ;
165
78
}
166
79
167
- // selectors
168
-
169
- function getTodosAsIds ( state ) {
170
- return state . todoState . ids
171
- . map ( id => state . todoState . entities [ id ] )
172
- . filter ( VISIBILITY_FILTERS [ state . filterState ] )
173
- . map ( todo => todo . id ) ;
174
- }
175
-
176
- function getTodo ( state , todoId ) {
177
- return state . todoState . entities [ todoId ] ;
178
- }
179
-
180
- function getNotifications ( state ) {
181
- return getArrayOfObject ( state . notificationState ) ;
182
- }
183
-
184
- function getArrayOfObject ( object ) {
185
- return Object . keys ( object ) . map ( key => object [ key ] ) ;
186
- }
187
-
188
- // sagas
189
-
190
- function * watchAddTodoWithNotification ( ) {
191
- yield takeEvery ( TODO_ADD_WITH_NOTIFICATION , handleAddTodoWithNotification ) ;
192
- }
193
-
194
- function * handleAddTodoWithNotification ( action ) {
195
- const { todo } = action ;
196
- const { id, name } = todo ;
197
- yield put ( doAddTodo ( id , name ) ) ;
198
- yield delay ( 5000 ) ;
199
- yield put ( doHideNotification ( id ) ) ;
200
- }
201
-
202
80
// store
203
81
204
82
const rootReducer = combineReducers ( {
205
83
todoState : todoReducer ,
206
84
filterState : filterReducer ,
207
- notificationState : notificationReducer ,
208
85
} ) ;
209
86
210
- const logger = createLogger ( ) ;
211
- const saga = createSagaMiddleware ( ) ;
87
+ const store = createStore ( rootReducer ) ;
212
88
213
- const store = createStore (
214
- rootReducer ,
215
- undefined ,
216
- applyMiddleware ( saga , logger )
217
- ) ;
218
-
219
- saga . run ( watchAddTodoWithNotification ) ;
220
-
221
- // components
89
+ // view layer
222
90
223
91
function TodoApp ( ) {
224
- return (
225
- < div >
226
- < ConnectedFilter />
227
- < ConnectedTodoCreate />
228
- < ConnectedTodoList />
229
- < ConnectedNotifications />
230
- </ div >
231
- ) ;
232
- }
233
-
234
- function Notifications ( { notifications } ) {
235
- return (
236
- < div >
237
- { notifications . map ( note => < div key = { note } > { note } </ div > ) }
238
- </ div >
239
- ) ;
240
- }
241
-
242
- function Filter ( { onSetFilter } ) {
243
- return (
244
- < div >
245
- Show
246
- < button
247
- type = "text"
248
- onClick = { ( ) => onSetFilter ( 'SHOW_ALL' ) } >
249
- All</ button >
250
- < button
251
- type = "text"
252
- onClick = { ( ) => onSetFilter ( 'SHOW_COMPLETED' ) } >
253
- Completed</ button >
254
- < button
255
- type = "text"
256
- onClick = { ( ) => onSetFilter ( 'SHOW_INCOMPLETED' ) } >
257
- Incompleted</ button >
258
- </ div >
259
- ) ;
260
- }
261
-
262
- class TodoCreate extends React . Component {
263
- constructor ( props ) {
264
- super ( props ) ;
265
-
266
- this . state = {
267
- value : '' ,
268
- } ;
269
-
270
- this . onCreateTodo = this . onCreateTodo . bind ( this ) ;
271
- this . onChangeName = this . onChangeName . bind ( this ) ;
272
- }
273
-
274
- onChangeName ( event ) {
275
- this . setState ( { value : event . target . value } ) ;
276
- }
277
-
278
- onCreateTodo ( event ) {
279
- this . props . onAddTodo ( this . state . value ) ;
280
- this . setState ( { value : '' } ) ;
281
- event . preventDefault ( ) ;
282
- }
283
-
284
- render ( ) {
285
- return (
286
- < div >
287
- < form onSubmit = { this . onCreateTodo } >
288
- < input
289
- type = "text"
290
- placeholder = "Add Todo..."
291
- value = { this . state . value }
292
- onChange = { this . onChangeName }
293
- />
294
- < button type = "submit" > Add</ button >
295
- </ form >
296
- </ div >
297
- ) ;
298
- }
299
- }
300
-
301
- function TodoList ( { todosAsIds } ) {
302
- return (
303
- < div >
304
- { todosAsIds . map ( todoId => < ConnectedTodoItem
305
- key = { todoId }
306
- todoId = { todoId }
307
- /> ) }
308
- </ div >
309
- ) ;
92
+ return < div > Todo App</ div > ;
310
93
}
311
94
312
- function TodoItem ( { todo, onToggleTodo } ) {
313
- const { name, id, completed } = todo ;
314
- return (
315
- < div >
316
- { name }
317
- < button
318
- type = "button"
319
- onClick = { ( ) => onToggleTodo ( id ) }
320
- >
321
- { completed ? "Incomplete" : "Complete" }
322
- </ button >
323
- </ div >
324
- ) ;
325
- }
326
-
327
- // Connecting React and Redux
328
-
329
- function mapStateToPropsList ( state ) {
330
- return {
331
- todosAsIds : getTodosAsIds ( state ) ,
332
- } ;
333
- }
334
-
335
- function mapStateToPropsItem ( state , props ) {
336
- return {
337
- todo : getTodo ( state , props . todoId ) ,
338
- } ;
339
- }
340
-
341
- function mapDispatchToPropsItem ( dispatch ) {
342
- return {
343
- onToggleTodo : id => dispatch ( doToggleTodo ( id ) ) ,
344
- } ;
345
- }
346
-
347
- function mapDispatchToPropsCreate ( dispatch ) {
348
- return {
349
- onAddTodo : name => dispatch ( doAddTodoWithNotification ( uuid ( ) , name ) ) ,
350
- } ;
351
- }
352
-
353
- function mapDispatchToPropsFilter ( dispatch ) {
354
- return {
355
- onSetFilter : filterType => dispatch ( doSetFilter ( filterType ) ) ,
356
- } ;
357
- }
358
-
359
- function mapStateToPropsNotifications ( state , props ) {
360
- return {
361
- notifications : getNotifications ( state ) ,
362
- } ;
363
- }
364
-
365
- const ConnectedTodoList = connect ( mapStateToPropsList ) ( TodoList ) ;
366
- const ConnectedTodoItem = connect ( mapStateToPropsItem , mapDispatchToPropsItem ) ( TodoItem ) ;
367
- const ConnectedTodoCreate = connect ( null , mapDispatchToPropsCreate ) ( TodoCreate ) ;
368
- const ConnectedFilter = connect ( null , mapDispatchToPropsFilter ) ( Filter ) ;
369
- const ConnectedNotifications = connect ( mapStateToPropsNotifications ) ( Notifications ) ;
370
-
371
- ReactDOM . render (
372
- < Provider store = { store } >
373
- < TodoApp />
374
- </ Provider > ,
375
- document . getElementById ( 'root' )
376
- ) ;
95
+ ReactDOM . render ( < TodoApp /> , document . getElementById ( 'root' ) ) ;
0 commit comments