11import React from 'react' ;
22import 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' ;
104import './index.css' ;
115
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-
246// action types
257
268const TODO_ADD = 'TODO_ADD' ;
279const TODO_TOGGLE = 'TODO_TOGGLE' ;
2810const FILTER_SET = 'FILTER_SET' ;
29- const NOTIFICATION_HIDE = 'NOTIFICATION_HIDE' ;
30- const TODO_ADD_WITH_NOTIFICATION = 'TODO_ADD_WITH_NOTIFICATION' ;
3111
3212// reducers
3313
3414const 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' } ,
5617] ;
5718
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 ) {
6620 switch ( action . type ) {
6721 case TODO_ADD : {
6822 return applyAddTodo ( state , action ) ;
@@ -75,18 +29,16 @@ function todoReducer(state = initialTodoState, action) {
7529}
7630
7731function 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 ) ;
8234}
8335
8436function 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+ ) ;
9042}
9143
9244function filterReducer ( state = 'SHOW_ALL' , action ) {
@@ -102,47 +54,8 @@ function applySetFilter(state, action) {
10254 return action . filter ;
10355}
10456
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-
13057// action creators
13158
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-
14659function doAddTodo ( id , name ) {
14760 return {
14861 type : TODO_ADD ,
@@ -164,213 +77,19 @@ function doSetFilter(filter) {
16477 } ;
16578}
16679
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-
20280// store
20381
20482const rootReducer = combineReducers ( {
20583 todoState : todoReducer ,
20684 filterState : filterReducer ,
207- notificationState : notificationReducer ,
20885} ) ;
20986
210- const logger = createLogger ( ) ;
211- const saga = createSagaMiddleware ( ) ;
87+ const store = createStore ( rootReducer ) ;
21288
213- const store = createStore (
214- rootReducer ,
215- undefined ,
216- applyMiddleware ( saga , logger )
217- ) ;
218-
219- saga . run ( watchAddTodoWithNotification ) ;
220-
221- // components
89+ // view layer
22290
22391function 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 > ;
31093}
31194
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