├── .gitignore ├── .jshintrc ├── images ├── EGH_Redux_Notes.png └── EGH_Redux_Notes_500.png ├── book.json ├── package.json ├── 21-Passing_the_Store_Down_with_Provider_from_React_Redux.md ├── README.md ├── 10-Reducer_Composition_with_combineReducers.md ├── 03-Implementing_Store_from_Scratch.md ├── SUMMARY.md ├── 09-Reducer_Composition_with_Objects.md ├── 06-Avoiding_Object_Mutations.md ├── 04-React_Counter_Example.md ├── 11-Implementing_combineReducers_from_Scratch.md ├── 08-Reducer_Composition_with_Arrays.md ├── 05-Avoiding_Array_Mutations.md ├── 24-Generating_Containers_with_connect_from_Readct_Redux_FooterLink.md ├── 13-React_Todo_List_Example_Toggling_a_Todo.md ├── 07-Writing_a_Todo_List_Reducer.md ├── 23-Generating_Containers_with_connect_from_React_Redux_AddTodo.md ├── 18-Extracting_Container_Components_VisibileTodoList__AddTodo.md ├── 25-Extracting_Action_Creators.md ├── 22-Generating_Containers_with_connect_from_React_Redux_VisibleTodoList.md ├── 02-Reducer_and_Store.md ├── 19-Passing_the_Store_Down_Explicitly_via_Props.md ├── 01-Intro_and_3_Principles_of_Redux.md ├── 20-Passing_the_Store_Down_Implicitly_via_Context.md ├── 17-Extracting_Container_Components_FilterLink.md ├── 15-Extracting_Presentational_Components_Todo__TodoList.md ├── 12-React_Todo_List_Example_Adding_a_Todo.md ├── 14-React_Todo_List_Example_Filtering_Todos.md └── 16-Extracting_Presentational_Components_AddTodo__Footer__FilterLink.md /.gitignore: -------------------------------------------------------------------------------- 1 | _book/ 2 | node_modules/ -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "newcap": false 6 | } 7 | -------------------------------------------------------------------------------- /images/EGH_Redux_Notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tayiorbeii/egghead.io_redux_course_notes/HEAD/images/EGH_Redux_Notes.png -------------------------------------------------------------------------------- /images/EGH_Redux_Notes_500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tayiorbeii/egghead.io_redux_course_notes/HEAD/images/EGH_Redux_Notes_500.png -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "egghead.io - Redux Fundamentals Course Guide", 3 | "description": "This repo contains notes from [Dan Abramov](https://github.com/gaearon)'s _excellent_ [Redux video series](https://egghead.io/lessons/javascript-redux-the-single-immutable-state-tree).", 4 | "plugins" : ["advanced-emoji","prism", "-highlight", "hints"] 5 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "egghead.io_redux_course_notes", 3 | "version": "1.0.0", 4 | "description": "This repo contains notes from [Dan Abramov](https://github.com/gaearon)'s _excellent_ [Redux video series](https://egghead.io/lessons/javascript-redux-the-single-immutable-state-tree).", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/eggheadio/egghead.io_redux_course_notes.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/eggheadio/egghead.io_redux_course_notes/issues" 18 | }, 19 | "homepage": "https://github.com/eggheadio/egghead.io_redux_course_notes#readme", 20 | "dependencies": { 21 | "express": "^4.13.4", 22 | "gitbook-plugin-advanced-emoji": "^0.1.6", 23 | "gitbook-plugin-hints": "^1.0.1", 24 | "gitbook-plugin-prism": "^1.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /21-Passing_the_Store_Down_with_Provider_from_React_Redux.md: -------------------------------------------------------------------------------- 1 | # 26. Passing the Store Down with `` from React Redux 2 | [Video Link](https://egghead.io/lessons/javascript-redux-passing-the-store-down-with-provider-from-react-redux) 3 | 4 | In the last section, we implemented a `Provider` component to pass `store` implicitly with React's `context` feature. It was really convenient. 5 | 6 | In fact, it was so convenient that we don't need to write `Provider` ourselves-- we can import it from the `react-redux` library that gives React bindings to the Redux library. 7 | 8 | Start by importing `Provider` from `'react-redux'` 9 | 10 | ```JavaScript 11 | // CDN style 12 | const { Provider } = ReactRedux; 13 | // npm style 14 | import { Provider } from 'react-redux'; 15 | ``` 16 | 17 | Just like the `Provider` we wrote before, the `Provider` that comes with `react-redux` exposes the `store` as a prop on the context. 18 | 19 |

20 | <- Prev 21 | Next -> 22 |

23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./images/EGH_Redux_Notes_500.png) 2 | 3 | # Egghead.io Redux Course Notes 4 | This repo contains notes from [Dan Abramov](https://github.com/gaearon)'s _excellent_ [Redux video series](https://egghead.io/lessons/javascript-redux-the-single-immutable-state-tree). 5 | 6 | These notes contain a lot of verbatim transcriptions, along with additional rewrites, links, etc. that have been added along the way. Feel free to submit additions to these notes, but please don't remove anything (unless we messed up or misunderstood something). 7 | 8 | Some of these documents contain multiple sections, but the majority are "one doc per vid" (there are 30 videos covered in 25 pages). Each section contains a link to the video, and towards the end of the series, timestamped links to Dan's "what we just did" recaps at the end of each lesson have been added. 9 | 10 | For a working final product of these lessons, visit [@sadams' `todo-redux-react-webpack` repo](https://github.com/sadams/todo-redux-react-webpack). 11 | 12 | ### Feel free to submit a PR! 13 | 14 | ### Gitbook 15 | 16 | ``` 17 | npm i -g gitbook-cli 18 | npm install 19 | gitbook install 20 | gitbook serve 21 | ``` 22 | -------------------------------------------------------------------------------- /10-Reducer_Composition_with_combineReducers.md: -------------------------------------------------------------------------------- 1 | # Reducer Composition with `combineReducers()` 2 | [Video Link](https://egghead.io/lessons/javascript-redux-reducer-composition-with-combinereducers) 3 | 4 | Since reducer composition is so common in Redux, there's a helper function `combineReducers()` 5 | 6 | So now instead of this: 7 | ```JavaScript 8 | const todoApp = (state = {}, action) => { 9 | return { 10 | todos: todos( 11 | state.todos, 12 | action 13 | ), 14 | visibilityFilter: visibilityFilter( 15 | state.visibilityFilter, 16 | action 17 | ) 18 | }; 19 | }; 20 | ``` 21 | 22 | We can do this: 23 | 24 | ```JavaScript 25 | const { combineReducers } = Redux; // CDN Redux import 26 | 27 | const todoApp = combineReducers({ 28 | todos: todos, 29 | visibilityFilter: visibilityFilter 30 | }); 31 | ``` 32 | 33 | This code generates our top level reducer. The only argument to `combineReducers()` is an object that specifies the mapping between the state field names and the reducers that manage them. 34 | 35 | The return value of `combineReducers()` is a reducer function that is pretty much equivalent to what we wrote earlier. 36 | 37 | **By convention, the state keys should be named after the reducers that manage them.** Because of this, we can use ES6 object literal shorthand notation to accomplish the same thing like this: 38 | 39 | ```JavaScript 40 | const todoApp = combineReducers({ 41 | todos, 42 | visibilityFilter 43 | }); 44 | ``` 45 | 46 | 47 |

48 | <- Prev 49 | Next -> 50 |

51 | -------------------------------------------------------------------------------- /03-Implementing_Store_from_Scratch.md: -------------------------------------------------------------------------------- 1 | # 07. Implementing Store from Scratch 2 | [Video Link](https://egghead.io/lessons/javascript-redux-implementing-store-from-scratch) 3 | 4 | We just used the `createStore()` function, but in order to understand it better, let's write it from scratch! 5 | 6 | 7 | We know that we need to pass in the `reducer` function, and that `getState()` will return the current value of `state`. We also know that we need to be able to dispatch actions, and subscribe. 8 | 9 | Because the `subscribe()` function can be called many times, we need to keep track of all the change listeners, so we create an array of listeners; everytime `subscribe()` is invoked we will push in the new listener to the array. 10 | 11 | Dispatching an action is the only way to change the internal state, so we calculate the new state as the result of calling `reducer()` with the current `state` and the `action` being dispatched. After the `state` is updated, we notify every change listener by calling them. 12 | 13 | In order to unsubscribe an event listener, we'll return a function from the `subscribe()` method that removes the listener from the listeners array. 14 | 15 | By the time the store is returned, we want the initial state to be populated. We're going to dispatch a dummy action just to get the reducer to return the initial value. 16 | 17 | ```Javascript 18 | const createStore = (reducer) => { 19 | let state; 20 | let listeners = []; 21 | 22 | const getState = () => state; 23 | 24 | const dispatch = (action) => { 25 | state = reducer(state, action); 26 | listeners.forEach(listener => listener()); 27 | }; 28 | 29 | const subscribe = (listener) => { 30 | listeners.push(listener); 31 | return () => { 32 | listeners = listeners.filter(l => l !== listener); 33 | } 34 | }; 35 | 36 | dispatch({}); // dummy dispatch 37 | 38 | return { getState, dispatch, subscribe }; 39 | 40 | }; 41 | ``` 42 | 43 | That's pretty much it! 44 | 45 |

46 | <- Prev 47 | Next -> 48 |

49 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Intro and 3 Principles of Redux](01-Intro_and_3_Principles_of_Redux.md) 4 | * [The Reducer and the Store](02-Reducer_and_Store.md) 5 | * [Implementing the Store from Scratch](03-Implementing_Store_from_Scratch.md) 6 | * [A React Counter Example](04-React_Counter_Example.md) 7 | * [Avoiding Array Mutations](05-Avoiding_Array_Mutations.md) 8 | * [Avoiding Object Mutations](06-Avoiding_Object_Mutations.md) 9 | * [Writing a Todo List Reducer](07-Writing_a_Todo_List_Reducer.md) 10 | * [Reducer Composition with Arrays](08-Reducer_Composition_with_Arrays.md) 11 | * [Reducer Composition with Objects](09-Reducer_Composition_with_Objects.md) 12 | * [Reducer Composition with combineReducers](10-Reducer_Composition_with_combineReducers.md) 13 | * [Implementing combineReducers() from Scratch](11-Implementing_combineReducers_from_Scratch.md) 14 | * [Example: Adding a Todo](12-React_Todo_List_Example_Adding_a_Todo.md) 15 | * [Example: Toggling a Todo](13-React_Todo_List_Example_Toggling_a_Todo.md) 16 | * [Example: Filtering Todos](14-React_Todo_List_Example_Filtering_Todos.md) 17 | * [Extracting Presentational Components: Todo and TodoList](15-Extracting_Presentational_Components_Todo__TodoList.md) 18 | * [Extracting Presentational Components: AddTodo, Footer, and FilterLink](16-Extracting_Presentational_Components_AddTodo__Footer__FilterLink.md) 19 | * [Extracting Container Components: Filter Link](17-Extracting_Container_Components_FilterLink.md) 20 | * [Extracting Container Components: VisibleTodoList and AddTodo](18-Extracting_Container_Components_VisibileTodoList__AddTodo.md) 21 | * [Passing the Redux Store Explicitly via Props](19-Passing_the_Store_Down_Explicitly_via_Props.md) 22 | * [Passing the Redux Store Explicitly via Context](20-Passing_the_Store_Down_Implicitly_via_Context.md) 23 | * [Passing the Store via Provider from react-redux](21-Passing_the_Store_Down_with_Provider_from_React_Redux.md) 24 | * [Generating Containers with connect(): VisibleTodoList](22-Generating_Containers_with_connect_from_React_Redux_VisibleTodoList.md) 25 | * [Generating Containers with connect(): AddTodo](23-Generating_Containers_with_connect_from_React_Redux_AddTodo.md) 26 | * [Generating Containers with connect(): FooterLink](24-Generating_Containers_with_connect_from_Readct_Redux_FooterLink.md) 27 | * [Extracting Action](25-Extracting_Action_Creators.md) 28 | -------------------------------------------------------------------------------- /09-Reducer_Composition_with_Objects.md: -------------------------------------------------------------------------------- 1 | # 14. Reducer Composition with Objects 2 | [Video Link](https://egghead.io/lessons/javascript-redux-reducer-composition-with-objects) 3 | 4 | In the last section, we used reducer composition to manage Todos in an array. 5 | 6 | While storing the application's state with just an array may work for small applications, we can use objects to store more information. 7 | 8 | For example, we can add a visibility filter to our Todo application. The state of the visibility filter is a simple string representing the current filter. The filter is changed via the `SET_VISIBILITY_FILTER` action. 9 | ```JavaScript 10 | const visibilityFilter = ( 11 | state = 'SHOW_ALL', 12 | action 13 | ) => { 14 | switch (action.type) { 15 | case 'SET_VISIBILITY_FILTER': 16 | return action.filter; 17 | default: 18 | return state; 19 | } 20 | }; 21 | ``` 22 | 23 | **To store this new information, we don't need to change the existing reducers.** 24 | 25 | We will use reducer composition to create a new reducer that calls existing reducers to manage their parts of the state, then combine the parts into a single state object. 26 | 27 | ```JavaScript 28 | const todoApp = (state = {}, action) => { 29 | return { 30 | // Call the `todos()` reducer from last section 31 | todos: todos( 32 | state.todos, 33 | action 34 | ), 35 | visibilityFilter: visibilityFilter( 36 | state.visibilityFilter, 37 | action 38 | ) 39 | }; 40 | }; 41 | ``` 42 | Note that the first time it runs, it will pass `undefined` as the `state` to the child reducers because the initial state of the combined reducer is an empty object, so all its fields are undefined. Remember that when we call reducers with `undefined` that they return their initial states, thus populating the `state` for the first time. 43 | 44 | When an action comes in, it calls the reducers with the parts of the state that they manage & the action then combines the results into the new `state` object. 45 | 46 | Now that we have composed this new `todoApp` reducer, we will use it to create the store: 47 | ```JavaScript 48 | // You've already imported Redux earlier in the app... 49 | const store = createStore(todoApp); 50 | ``` 51 | 52 | This pattern helps to scale Redux development, since different team members can work on different reducers that work with the same actions, without stepping on each other's toes. 53 | 54 | 55 |

56 | <- Prev 57 | Next -> 58 |

59 | -------------------------------------------------------------------------------- /06-Avoiding_Object_Mutations.md: -------------------------------------------------------------------------------- 1 | # 10. Avoiding Object Mutations with `Object.assign()` and `...spread` 2 | [Video Link](https://egghead.io/lessons/javascript-redux-avoiding-object-mutations-with-object-assign-and-spread) 3 | 4 | Like the previous example, this code uses the Expect and DeepFreeze libraries. 5 | 6 | We are going to test a function `toggleTodo()` that takes a Todo item and toggles its "completed" field. 7 | 8 | ```Javascript 9 | const toggleTodo = (todo) => { 10 | // Mutated version: 11 | todo.completed = !todo.completed 12 | return todo; 13 | } 14 | 15 | const testToggleTodo = () => { 16 | const todoBefore = { 17 | id: 0, 18 | text: 'Learn Redux', 19 | completed: false 20 | }; 21 | const todoAfter = { 22 | id: 0, 23 | text: 'Learn Redux', 24 | completed: true 25 | }; 26 | 27 | deepFreeze(todoBefore); 28 | 29 | expect( 30 | toggleTodo(todoBefore) 31 | ).toEqual(todoAfter); 32 | }; 33 | 34 | testToggleTodo(); 35 | console.log('All tests passed.'); 36 | ``` 37 | 38 | One way to do this without mutation is to copy the object with the "completed" value flipped: 39 | ```Javascript 40 | const toggleTodo = (todo) => { 41 | return { 42 | id: todo.id, 43 | text: todo.text, 44 | completed: !todo.completed 45 | }; 46 | } 47 | ``` 48 | 49 | However, if we add new properties later, we may forget to update this piece of code. This is why we should use ES6's `Object.assign()`. This lets you assign properties of several objects onto the target object. 50 | 51 | ```Javascript 52 | const toggleTodo = (todo) => { 53 | return Object.assign({}, todo, { 54 | completed: !todo.completed 55 | }); 56 | }; 57 | ``` 58 | _Note how the argument order to `Object.assign()` corresponds to the JavaScript assignment operator order._ 59 | 60 | The left argument is the one whose properties are going to be assigned, so we pass in an empty object (`{}`) because it will be mutated (remember, we don't want to mutate any existing data). 61 | 62 | Every further argument to `Object.assign()` is considered a source object whose properties will be copied to the target (in this case, the target is our blank object provided as the first argument). 63 | 64 | If there are multiple occurrences of the same property/properties, the last occurrence "wins". In this case, the `{ completed: !todo.completed }` we specify in the third overwrites the `completed` contained within the `todo` in the second argument. 65 | 66 | _Remember that ES6 need to be transpiled (at least for the time being)..._ 67 | 68 | Another option to do the same thing is with the `spread` operator proposed for ES7: 69 | ```Javascript 70 | const toggleTodo = (todo) => { 71 | return { 72 | ...todo, 73 | completed: !todo.completed 74 | }; 75 | }; 76 | ``` 77 | 78 | 79 |

80 | <- Prev 81 | Next -> 82 |

83 | -------------------------------------------------------------------------------- /04-React_Counter_Example.md: -------------------------------------------------------------------------------- 1 | # 08. React Counter Example 2 | [Video Link](https://egghead.io/lessons/javascript-redux-react-counter-example) 3 | 4 | In the simple example, we were updating the document body every time data in the store's state changed. Of course, this doesn't scale. So we'll use React. 5 | 6 | _Note that React has been included from CDN in the video example. We will also be rendering to `
`._ 7 | 8 | With React, we refactor our code a bit. We create a `Counter` component and render it to the `root div`. 9 | 10 | Note that `store.getState()` is passed as a prop to the `Counter` element as the render function will be called 11 | any time the store's state changes. 12 | 13 | This also allows the Counter to be refactored to a simple function (supported in react 0.14) 14 | 15 | ```Javascript 16 | // ... store created with `counter` reducer ... 17 | 18 | const Counter = ({ value }) => ( 19 |

{value}

20 | ); 21 | 22 | const render = () => { 23 | ReactDOM.render( 24 | , 25 | document.getElementById('root') 26 | ); 27 | }; 28 | 29 | store.subscribe(render); 30 | render(); 31 | ``` 32 | 33 | We can add "Increment" and "Decrement" buttons to the `Counter` without re-introducing the Redux dependency, 34 | so the onIncrement/onDecrement callbacks may instead be passed as props to the button(s). 35 | 36 | ```Javascript 37 | const Counter = ({ 38 | value, 39 | onIncrement, 40 | onDecrement 41 | }) => ( 42 |
43 |

{value}

44 | 45 | 46 |
47 | ); 48 | 49 | const render = () => { 50 | ReactDOM.render( 51 | 54 | store.dispatch({ 55 | type: 'INCREMENT' 56 | }) 57 | } 58 | onDecrement={() => 59 | store.dispatch({ 60 | type: 'DECREMENT' 61 | }) 62 | } 63 | />, 64 | document.getElementById('root') 65 | ); 66 | } 67 | ``` 68 | 69 | 70 | The `Counter` component is a "dumb" component. It doesn't contain any business logic. A dumb component only specifies how the current state is rendered into output, and how the callbacks passed via props are bound to the event handlers. 71 | 72 | #### To recap how it works... 73 | 74 | When the `Counter` is rendered, we specify that its value should be taken from the Redux store's current state. When the user presses a button, the corresponding action is dispatched to the Redux store. 75 | 76 | The reducer specifies how the next state is calculated based on the current state and the action being dispatched. 77 | 78 | Finally, we subscribe to the Redux store so our `render()` function runs any time the state changes so our `Counter` gets the current state. 79 | 80 |

81 | <- Prev 82 | Next -> 83 |

84 | -------------------------------------------------------------------------------- /11-Implementing_combineReducers_from_Scratch.md: -------------------------------------------------------------------------------- 1 | # 16. Implementing `combineReducers()` from Scratch 2 | [Video Link](https://egghead.io/lessons/javascript-redux-implementing-combinereducers-from-scratch) 3 | 4 | Now that we know how to use `combineReducers()` to save us some time, let's implement it from scratch in order to understand it deeper. 5 | 6 | Recall that the only argument to `combineReducers()` is the mapping between the state keys and their corresponding reducers, so we'll start by writing a function that accepts a parameter we'll call "reducers". 7 | 8 | Since `combineReducers()` returns a reducer function, it must have the signature of a reducer function (the state and an action). 9 | 10 | Inside of our return reducer, we call the `Object.keys()` function to get all the keys from our `reducers` object (in our example, this is `todos` and `visibilityFilter`). 11 | 12 | We then run `reduce()` on the keys because we want to produce a single value that represents the next state. We do this by accumulating over every key and running its associated reducer. 13 | 14 | Since each reducer that is run through `combineReducers()` is responsible for only part of the application's overall state, we say that the next state for a given key can be calculated by calling the corresponding reducer for the given key with the current state (for the given key) & the action. 15 | 16 | The array reduce wants us to return the next accumulated value from the callback (i.e. `nextState`). We also specify an empty object as the initial next state before all the keys are processed. 17 | 18 | ```Javascript 19 | const combineReducers = reducers => { 20 | return (state = {}, action) => { 21 | 22 | // Reduce all the keys for reducers from `todos` and `visibilityFilter` 23 | return Object.keys(reducers).reduce( 24 | (nextState, key) => { 25 | // Call the corresponding reducer function for a given key 26 | nextState[key] = reducers[key] ( 27 | state[key], 28 | action 29 | ); 30 | return nextState; 31 | }, 32 | {} // The `reduce` on our keys gradually fills this empty object until it is returned. 33 | ); 34 | }; 35 | }; 36 | ``` 37 | 38 | Call combineReducers with an object whose values are the reducer functions and keys are state fields they manage. 39 | 40 | ```JavaScript 41 | const todoApp = combineReducers({ 42 | todos, 43 | visibilityFilter 44 | }); 45 | ``` 46 | 47 | 48 | It's okay that we are mutating the empty object representing `nextState`, because it was created within the `combineReducers()` function and not passed in from the outside. Thus, our function remains pure. 49 | 50 | It's important to understand functional programming-- functions can take other functions as arguments, and return other functions. Knowing this will increase productivity with Redux in the long term. 51 | 52 | 53 |

54 | <- Prev 55 | Next -> 56 |

57 | -------------------------------------------------------------------------------- /08-Reducer_Composition_with_Arrays.md: -------------------------------------------------------------------------------- 1 | # 13. Reducer Composition with Arrays 2 | [Video Link](https://egghead.io/lessons/javascript-redux-reducer-composition-with-arrays) 3 | 4 | We left off with code for a `todos` reducer. 5 | 6 | The code is somewhat difficult to read because it mixes 2 different concerns: updating the todos array, as well as updating an individual todo item: 7 | 8 | ```JavaScript 9 | const todos = (state = [], action) => { 10 | switch (action.type) { 11 | case 'ADD_TODO': 12 | return [ 13 | ...state, 14 | { 15 | id: action.id, 16 | text: action.text, 17 | completed: false 18 | } 19 | ]; 20 | case 'TOGGLE_TODO': 21 | return state.map(todo => { 22 | if (todo.id !== action.id) { 23 | return todo; 24 | } 25 | 26 | return { 27 | ...todo, 28 | completed: !todo.completed 29 | }; 30 | }); 31 | } 32 | } 33 | ``` 34 | 35 | Every time a function does too many things, it's best to break them up into other functions that each address only one concern. 36 | 37 | In this case, "creating and updating a todo" is a separate task to undertake, so we'll bring this code into a new function that has two arguments: the current state, and the action being dispatched. 38 | 39 | Note that in this new function that `state` refers to the individual todo, and not the list of todos. 40 | ```Javascript 41 | const todo = (state, action) => { 42 | switch (action.type) { 43 | case 'ADD_TODO': 44 | return { 45 | id: action.id, 46 | text: action.text, 47 | completed: false 48 | }; 49 | case 'TOGGLE_TODO': 50 | if (state.id !== action.id) { 51 | return state; 52 | } 53 | 54 | return { 55 | ...state, 56 | completed: !state.completed 57 | }; 58 | default: 59 | return state; 60 | } 61 | } 62 | ``` 63 | 64 | Now that we've extracted our `todo` reducer from our `todos` reducer, we have to call it for every todo item and assemble the results into an array: 65 | 66 | ```JavaScript 67 | const todos = (state = [], action) => { 68 | switch (action.type) { 69 | case 'ADD_TODO': 70 | return [ 71 | ...state, 72 | todo(undefined, action) 73 | ]; 74 | case 'TOGGLE_TODO': 75 | return state.map(t => todo(t, action)); 76 | default: 77 | return state; 78 | } 79 | }; 80 | ``` 81 | _Remember to have a `default` case where `state` is returned to avoid odd bugs in the future._ 82 | 83 | What we've just done is a common Redux practice called **reducer composition**. Different reducers specify how different parts of the state tree are updated in response to actions. Since reducers are normal JS functions, they can call other reducers to delegate & abstract away updates to the state. 84 | 85 | Reducer composition can be applied many times. While there's a single top-level reducer managing the overall state of the app, it's encouraged to have reducers call each other as needed to manage the state tree. 86 | 87 | 88 |

89 | <- Prev 90 | Next -> 91 |

92 | -------------------------------------------------------------------------------- /05-Avoiding_Array_Mutations.md: -------------------------------------------------------------------------------- 1 | # 09. Avoiding Array Mutations 2 | [Video Link](https://egghead.io/lessons/javascript-redux-avoiding-array-mutations-with-concat-slice-and-spread) 3 | 4 | _Note: This code uses Expect and Deep-Freeze libraries for testing and mutation checking respectively._ 5 | 6 | Say we want to implement a counter list application. We will need to write a few functions to operate on its state, which is an array of numbers representing the individual counters. 7 | 8 | ```Javascript 9 | const addCounter = (list) => { 10 | list.push(0); 11 | return list; 12 | }; 13 | 14 | const testAddCounter = () => { 15 | const listBefore = []; 16 | const listAfter = [0]; 17 | 18 | deepFreeze(listBefore); 19 | 20 | expect( 21 | addCounter(listBefore) 22 | ).toEqual(listAfter); 23 | }; 24 | 25 | testAddCounter(); 26 | console.log('All tests passed') 27 | ``` 28 | 29 | As this code stands now, the test fails because we can't push 0 onto a frozen object. 30 | 31 | Instead, we need to use **concat**, because it doesn't modify the original object: 32 | ```Javascript 33 | const addCounter = (list) => { 34 | // return list.concat([0]); // old way 35 | return [...list, 0]; // ES6 way 36 | }; 37 | ``` 38 | 39 | In this application we also want to be able to remove counters: 40 | 41 | ```Javascript 42 | const removeCounter = (list, index) => { 43 | list.splice(index, 1); 44 | return list; 45 | } 46 | . 47 | . 48 | . 49 | const testRemoveCounter = () => { 50 | const listBefore = [0, 10, 20]; 51 | const listAfter = [0, 20]; 52 | 53 | expect ( 54 | removeCounter(listBefore, 1) 55 | ).toEqual(listAfter); 56 | }; 57 | ``` 58 | This works, but `splice` is also a mutating method. 59 | We need to use `slice` instead: 60 | 61 | ```Javascript 62 | const removeCounter = (list, index) => { 63 | // Old way: 64 | //return list 65 | // .slice(0, index) 66 | // .concat(list.slice(index + 1)); 67 | 68 | // ES6 way: 69 | return [ 70 | ...list.slice(0, index), 71 | ...list.slice(index + 1) 72 | ]; 73 | }; 74 | ``` 75 | 76 | Now let's implement incrementing the counter. The function will take in the array and the index of the counter that we are incrementing. 77 | ```Javascript 78 | const incrementCounter = (list, index) => { 79 | list[index]++; 80 | return list; 81 | }; 82 | 83 | const testIncrementCounter = () => { 84 | const listBefore = [0, 10, 20]; 85 | const listAfter = [0, 11, 20]; 86 | 87 | deepFreeze(listBefore); 88 | 89 | expect( 90 | incrementCounter(listBefore, 1) 91 | ).toEqual(listAfter); 92 | }; 93 | ``` 94 | 95 | This fails because we are mutating. The correct approach is similar to how we removed an item-- we will slice up to the item we want to increment, concat with a single item that we have incremented, then concat the rest of the original array. 96 | ```Javascript 97 | const incrementCounter = (list, index) => { 98 | // Old way: 99 | // return list 100 | // .slice(0, index) 101 | // .concat([list[index] + 1]) 102 | // .concat(list.slice(index + 1)); 103 | 104 | // ES6 way: 105 | return [ 106 | ...list.slice(0, index), 107 | list[index] + 1, 108 | ...list.slice(index + 1) 109 | ]; 110 | }; 111 | ``` 112 | 113 |

114 | <- Prev 115 | Next -> 116 |

117 | -------------------------------------------------------------------------------- /24-Generating_Containers_with_connect_from_Readct_Redux_FooterLink.md: -------------------------------------------------------------------------------- 1 | # 29. Generating Containers with `connect()` from React Redux (`FooterLink`) 2 | [Video Link](https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-footerlink) 3 | 4 | Now let's use `connect()` on our `FooterLink` component. 5 | 6 | Recall that our `FilterLink` component renders a `Link` with an `active` prop and an `onClick` handler. 7 | 8 | #### `mapStateToProps` 9 | 10 | We'll start by writing our `mapStateToProps` function which we will call `mapStateToLinkProps` because everything is in a single file, remember? It will accept the state of the Redux store, and return the props that should be passed to the `Link` component. 11 | 12 | The only prop in `Link` is `active`, which determines the styling based on the `visibilityFilter.` We remove the definition from `Link`'s `active` prop, and move it into `mapStateToLinkProps`. 13 | 14 | ```JavaScript 15 | const mapStateToLinkProps = ( 16 | state 17 | ) => { 18 | return { 19 | active: 20 | props.filter === 21 | state.visibilityFilter 22 | } 23 | }; 24 | ``` 25 | Notice that `active` now references the `filter` prop of the `FilterLink` component. In order to tell if a `Link` is active or not, we need to compare this prop with the `visibilityFilter` in the Redux store's state. 26 | 27 | It's common to use the container props when calculating the child props, so we pass them in as a second argument to `mapStateToProps`. In this case, we'll rename it to `ownProps` to make it more clear that we are talking about the container component's _own_ props, and not the props that are passed to the child, which is the return value of `mapStateToProps`. 28 | 29 | ```JavaScript 30 | const mapStateToLinkProps = ( 31 | state, 32 | ownProps 33 | ) => { 34 | return { 35 | active: 36 | ownProps.filter === 37 | state.visibilityFilter 38 | } 39 | }; 40 | ``` 41 | 42 | #### `mapDispatchToProps` 43 | Again, we will rename this function to `mapDispatchToLinkProps` to avoid name collisions. 44 | 45 | Initially we know our first argument is the `dispatch()` function. To see what other arguments we need, we will to look at the container component to see what props depend on the `dispatch` function. 46 | 47 | In this case, we only have the `onClick()` where we dispatch the action of type `'SET_VISIBILITY_FILTER'` along with the `filter` type. Since there is another reference to `props`, we will add `ownProps` as our second argument to `mapDispatchToLinkProps`. 48 | 49 | ```JavaScript 50 | const mapDispatchToLinkProps = ( 51 | dispatch, 52 | ownProps 53 | ) => { 54 | return { 55 | onClick: () => { 56 | dispatch({ 57 | type: 'SET_VISIBILITY_FILTER', 58 | filter: ownProps.filter 59 | }); 60 | } 61 | }; 62 | } 63 | ``` 64 | 65 | 66 | #### `connect()` it Up 67 | 68 | ```JavaScript 69 | const FilterLink = connect( 70 | mapStateToLinkProps, 71 | mapDispatchToLinkProps 72 | )(Link); 73 | ``` 74 | 75 | Now that we've used `react-redux`'s `connect()`, we can remove our old `FilterLink` implementation, including the `contextTypes`. 76 | 77 | [2:32 has the recap.](https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-footerlink) 78 | 79 |

80 | <- Prev 81 | Next -> 82 |

83 | -------------------------------------------------------------------------------- /13-React_Todo_List_Example_Toggling_a_Todo.md: -------------------------------------------------------------------------------- 1 | # 18. React Todo List Example (Toggling a Todo) 2 | [Video Link](https://egghead.io/lessons/javascript-redux-react-todo-list-example-toggling-a-todo) 3 | 4 | Building onto what we've been working on, it's time to dispatch our `'TOGGLE_TODO'` action. We will do this by clicking on the individual todo items in our bulleted list. 5 | 6 | Inside of our `TodoApp` component, we map each of the todo items into an `
  • `. We add a click handler so that when a user clicks on an item, we will dispatch an action to the store of type `'TOGGLE_TODO'` along with the `id` to be toggled (we get the `id` from the `todo` object.) 7 | 8 | In the UI, we want the todo item to appear as crossed out if it has been completed, so we'll use the `textDecoration` style property. 9 | 10 | ```Javascript 11 | . 12 | . // TodoApp component stuff 13 | . 14 |
      15 | {this.props.todos.map(todo => 16 |
    • { 18 | store.dispatch({ 19 | type: 'TOGGLE_TODO', 20 | id: todo.id 21 | }); 22 | }} 23 | style={{ 24 | textDecoration: 25 | todo.completed ? 26 | 'line-through' : 27 | 'none' 28 | }}> 29 | {todo.text} 30 |
    • 31 | )} 32 |
    33 | . 34 | . // More TodoApp component stuff 35 | . 36 | ``` 37 | 38 | #### Recap of how toggling a todo item works 39 | Inside the click handler, we dispatch the `'TOGGLE_TODO'` action with a type of `'TOGGLE_TODO'` and the `id` of the todo being rendered. 40 | 41 | When an action is dispatched, the store will call the root reducer, which will call the `todos()` reducer with the array of todos & the action. 42 | 43 | Since this action is of type `'TOGGLE_TODO'`, the `todos()` reducer delegates the handling of every todo item to the `todo()` reducer by using the `map()` function to call it for every todo item in `state`: 44 | 45 | ```Javascript 46 | const todos = (state = [], action) => { 47 | switch (action.type) { 48 | // case 'ADD_TODO' stuff 49 | case 'TOGGLE_TODO': 50 | return state.map(t => 51 | todo(t, action) 52 | ); 53 | // default case stuff 54 | } 55 | } 56 | ``` 57 | 58 | The `todo()` reducer receives the todo item as `state`, and 'TOGGLE_TODO' as `action`. For every todo item whose `id` doesn't match the `id` in the action (remember the action's `id` was supplied by clicking the `
  • `), we just return the previous state (i.e. the `todo` object as it was). 59 | 60 | However, if the `id` of the todo matches the `id` of the action, we'll use ES6 notation to return a new object with all the properties of the original todo, but with the `completed` field toggled. 61 | ```JavaScript 62 | const todo = (state, action) => { 63 | // case 'ADD_TODO' stuff 64 | case 'TOGGLE_TODO': 65 | if (state.id !== action.id) { 66 | return state; 67 | } 68 | 69 | return { 70 | ...state, 71 | completed: !state.completed 72 | }; 73 | // default case stuff 74 | }; 75 | ``` 76 | 77 | The updated todo item will be included in the `todos` field under the new application `state`, and because the `render()` function subscribes to the store, it's going to get the next state of the application via `store.getState()` and pass the new version of the `todos` array to the `TodoApp` component to be mapped and rendered as a bulleted list (where completed items have a line through them). 78 | 79 | ```JavaScript 80 | const render = () => { 81 | ReactDOM.render( 82 | , 85 | document.getElementById('root') 86 | ); 87 | }; 88 | 89 | store.subscribe(render); 90 | render(); 91 | ``` 92 | Thus, our cycle is complete again. 93 | 94 |

    95 | <- Prev 96 | Next -> 97 |

    98 | -------------------------------------------------------------------------------- /07-Writing_a_Todo_List_Reducer.md: -------------------------------------------------------------------------------- 1 | # 11. Writing a Todo List Reducer (Adding a Todo) 2 | [Video Link](https://egghead.io/lessons/javascript-redux-writing-a-todo-list-reducer-adding-a-todo) 3 | 4 | We are again using DeepFreeze and Expect. 5 | 6 | In this section we are going to write a reducer for our Todo List application. 7 | 8 | ```JavaScript 9 | const todos = (state = [], action) => { 10 | switch (action.type) { 11 | case 'ADD_TODO': 12 | return [ 13 | ...state, 14 | { 15 | id: action.id, 16 | text: action.text, 17 | completed: false 18 | } 19 | ]; 20 | default: 21 | return state; 22 | } 23 | }; 24 | 25 | const testAddTodo = () => { 26 | const stateBefore = []; 27 | const action = { 28 | type: 'ADD_TODO', 29 | id: 0, 30 | text: 'Learn Redux' 31 | }; 32 | const stateAfter = [{ 33 | id: 0, 34 | text: 'Learn Redux', 35 | completed: false 36 | }]; 37 | 38 | deepFreeze(stateBefore); 39 | deepFreeze(action); 40 | 41 | expect( 42 | todos(stateBefore, action) 43 | ).toEqual(stateAfter); 44 | }; 45 | 46 | testAddTodo(); 47 | console.log('All tests passed') 48 | ``` 49 | 50 | #### Data Flow: 51 | First, we create an empty state array (`stateBefore`) and our `action` object inside the test function. 52 | 53 | Next, they are passed into our `todos` reducer function, which notices that our action type of `'ADD_TODO'` is recognized. 54 | 55 | The reducer returns a new array containing the same items as the old array, as well as a new item representing the Todo we just added. Note that since we passed in an empty array (`stateBefore = []`) we are returned a single item array. 56 | 57 | Finally, our new array is compared with our expected array with our single Todo item. 58 | 59 | 60 | # 12. Writing a Todo List Reducer (Toggling a Todo) 61 | [Video Link](https://egghead.io/lessons/javascript-redux-writing-a-todo-list-reducer-toggling-a-todo) 62 | 63 | Now we will handle toggling Todos by adding to our above code. 64 | 65 | ```JavaScript 66 | const todos = (state = [], action) => { 67 | switch (action.type) { 68 | case 'ADD_TODO': 69 | // ... ADD_TODO logic as above 70 | case 'TOGGLE_TODO': 71 | return state.map(todo => { 72 | if (todo.id !== action.id) { 73 | return todo; 74 | } else { 75 | // for the todo that matches the action id return all other information the same 76 | // but change the completed property to the opposite of what it was previously 77 | return { 78 | ...todo, 79 | completed: !todo.completed 80 | }; 81 | } 82 | }); 83 | default: 84 | return state; 85 | } 86 | }; 87 | 88 | const testToggleTodo = () => { 89 | const stateBefore = [ 90 | { 91 | id: 0, 92 | text: 'Learn Redux', 93 | completed: false 94 | }, 95 | { 96 | id: 1, 97 | text: 'Go Shopping', 98 | completed: false 99 | } 100 | ]; 101 | const action = { 102 | type: 'TOGGLE_TODO', 103 | id: 1 104 | }; 105 | 106 | const stateAfter = [ 107 | { 108 | id: 0, 109 | text: 'Learn Redux', 110 | completed: false 111 | }, 112 | { 113 | id: 1, 114 | text: 'Go Shopping', 115 | completed: true 116 | } 117 | ]; 118 | 119 | deepFreeze(stateBefore); 120 | deepFreeze(action); 121 | 122 | expect( 123 | todos(stateBefore, action) 124 | ).toEqual(stateAfter); 125 | }; 126 | 127 | testAddTodo(); 128 | testToggleTodo(); 129 | console.log('All tests passed.'); 130 | ``` 131 | 132 | Note that inside our `'TOGGLE_TODO'` reducer that we return the existing todo item if the `id` doesn't match the `id` of the todo we are toggling. If the `id` does match, we use the spread operator to return a new object with all the properties of the existing `todo` object, along with an inverted `completed` value. 133 | 134 | 135 |

    136 | <- Prev 137 | Next -> 138 |

    139 | -------------------------------------------------------------------------------- /23-Generating_Containers_with_connect_from_React_Redux_AddTodo.md: -------------------------------------------------------------------------------- 1 | # 28. Generating Containers with `connect()` from React Redux (AddTodo) 2 | [Video Link](https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-addtodo) 3 | 4 | In the last section we used `connect()` to set up our `VisibleTodoList` component. 5 | 6 | Since these examples have all of our JavaScript written in a single file, we need to rename our `mapStateToProps` and `mapDispatchToProps` functions to be more specific. Note that this doesn't need to be done if we keep all of our components in separate files. 7 | 8 | Recall that our `AddTodo` component wasn't clearly a presentational or container component. However, it does rely on `store` for the `dispatch()` function. 9 | 10 | Instead of reading `store` from the context, we are going to refactor `AddTodo` to read `dispatch` from the props. This is because `AddTodo` only needs `dispatch()`, not the whole store. 11 | 12 | We will be creating a container component using `connect()` that will inject the dispatch function as a prop. We will remove `AddTodo.contextTypes` because the component generated by the `connect()` function will take care of reading the store from the context. 13 | 14 | 15 | ##### Before: 16 | ```JavaScript 17 | const AddTodo = (props, { store }) => { 18 | . 19 | . // inside `return` 20 | . 21 | 77 | 78 | ); 79 | }; 80 | ``` 81 | 82 | 83 | 84 | #### Refactored `TodoApp` 85 | Now that we've refactored our components, it's become clear that none of the containers need props from `TodoApp`! We can also get rid of `TodoApp`'s `render()` function that rendered the current state of the store. 86 | 87 | We can get rid of the `render()` function because the container components inside of `TodoApp` are now set up to get state and update themselves as needed, therefore, we 88 | only need to render `TodoApp` once on initialization. 89 | 90 | ```JavaScript 91 | const TodoApp = () => ( 92 |
    93 | 94 | 95 |
    96 |
    97 | ); 98 | 99 | // Note this render does not belong to `TodoApp` 100 | ReactDOM.render( 101 | , 102 | document.getElementById('root') 103 | ); 104 | ``` 105 | 106 | #### Recap of Data Flow 107 | [3:33 in the video](https://egghead.io/lessons/javascript-redux-extracting-container-components-visibletodolist-addtodo) 108 | 109 |

    110 | <- Prev 111 | Next -> 112 |

    113 | -------------------------------------------------------------------------------- /25-Extracting_Action_Creators.md: -------------------------------------------------------------------------------- 1 | # 30. Extracting Action Creators 2 | [Video Link](https://egghead.io/lessons/javascript-redux-extracting-action-creators) 3 | 4 | So far we've covered container components, presentational components, reducers, and the store... but we haven't covered *action creators*. 5 | 6 | In our current `AddTodo` component, we dispatch an action of type `'ADD_TODO'` when the "Add Todo" button is clicked. However, it references the `nextTodoId` variable which is declared alongside the component. Normally it would be local, but what if another component wants to be able to dispatch `'ADD_TODO'`? The component would need to have access to `nextTodoId`. 7 | 8 | 9 | ##### Existing `AddTodo` Code 10 | ```JavaScript 11 | let nextTodoId = 0; 12 | let AddTodo = ({ dispatch }) => { 13 | let input; 14 | 15 | return ( 16 |
    17 | { 18 | input = node; 19 | }} /> 20 | 30 |
    31 | ); 32 | }; 33 | ``` 34 | In order to allow other components to dispatch the `'ADD_TODO'` action, it would be best if `'ADD_TODO'` didn't have to worry about specifying the `id`. The only information that really is passed is the `text` of the todo being added. We don't want to generate the `id` inside of the reducer because that would make it [non-deterministic](https://en.wikipedia.org/wiki/Nondeterministic_algorithm). 35 | 36 | 37 | #### Action Creator 38 | 39 | Our first action creator will be `addTodo`. This will be a function that takes the `text` of the todo and constructs an action object representing the `'ADD_TODO'` action. 40 | 41 | Replace the code inside the `dispatch()` call inside of the `AddTodo` component with a call to `addTodo()`. 42 | ```JavaScript 43 | // inside `AddTodo` component 44 | 38 |
      39 | {visibleTodos.map(todo => 40 |
    • { 42 | store.dispatch({ 43 | type: 'TOGGLE_TODO', 44 | id: todo.id 45 | }); 46 | }} 47 | style={{ 48 | textDecoration: 49 | todo.completed ? 50 | 'line-through' : 51 | 'none' 52 | }} 53 | > 54 | {todo.text} 55 |
    • 56 | )} 57 |
    58 |

    59 | Show: 60 | {' '} 61 | 65 | All 66 | 67 | {', '} 68 | 72 | Active 73 | 74 | {', '} 75 | 79 | Completed 80 | 81 |

    82 | 83 | ); 84 | } 85 | } 86 | ``` 87 | #### Refactoring for a Single Todo Item 88 | First, we will extract the Todo component that renders a single list item. 89 | 90 | We will declare the Todo item as a function, which is available in React 14. We can remove the `key` property, since it's only needed when we enumerate an array (we'll use it later when we have to enumerate many todos). 91 | 92 | Previously we had hardcoded a click handler that dispatched `'TOGGLE_TODO'`. It's best practice with React to have several components that don't specify any behaviors, and only are concerned with how things are rendered (how they look). These are called **presentational components**. 93 | 94 | Because we want our list to be a presentational component, we "promote" the `onClick` handler to become a prop. 95 | 96 | We also want to be more explicit about what the data is that the component needs to render. Instead of passing a `todo` object, we will pass `completed` and `text` fields as separate props. 97 | 98 | ```JavaScript 99 | const Todo = ({ 100 | onClick, 101 | completed, 102 | text 103 | }) => ( 104 |
  • 113 | {text} 114 |
  • 115 | ); 116 | ``` 117 | 118 | Now our `Todo` component is purely presentational. It doesn't specify any behavior, but it knows how to render a single todo item. 119 | 120 | 121 | #### Refactoring for the Todo List 122 | The `TodoList` component will accept an array of todos, and will render them into a `
      ` by using the `todos.map()` function to render a `Todo` component for each todo item. 123 | 124 | We tell React to use each todo's `id` as the unique `key` for the elements, and we'll use the spread operator to send the `todo` object's `text` and `completed` properties are sent as props to the `Todo` component. 125 | 126 | We need to specify what happens when a `Todo` is clicked. Since we want to keep this as a presentational component, instead of dispatching an action, we'll specify a function `onTodoClick()` and pass it `todo.id` so it can decide what needs to happen. We will also pass `onClick` (which calls `onTodoClick()`) as a prop to the `Todo` component. 127 | 128 | ```JavaScript 129 | const TodoList = ({ 130 | todos, 131 | onTodoClick 132 | }) => ( 133 |
        134 | {todos.map(todo => 135 | onTodoClick(todo.id)} 139 | /> 140 | )} 141 |
      142 | ) 143 | ``` 144 | 145 | #### Container Components (`TodoApp`) 146 | While presentational components just display data, we need a way to actually pass data from the store. 147 | This is where **container components** come in-- they can specify behavior and pass data. 148 | 149 | In our example, `TodoApp` is our container component. 150 | 151 | Now that we've created `TodoList` and `Todo` presentational components, we can put them into our `TodoApp` container component. 152 | 153 | Our `TodoApp` will render our `TodoList` with `visibleTodos` as the `todos`, along with a callback that says when `onTodoClick` is called with a todo `id`, we should dispatch an action on the store of type `'TOGGLE_TODO'` along with the `id` of the todo. 154 | 155 | ```JavaScript 156 | class TodoApp extends Component { 157 | render () { 158 | const { 159 | todos, 160 | visibilityFilter 161 | } = this.props; 162 | 163 | const visibleTodos = getVisibleTodos( 164 | todos, 165 | visibilityFilter 166 | ); 167 | 168 | return ( 169 |
      170 | { 171 | this.input = node; 172 | }} /> 173 | 183 | 186 | store.dispatch({ 187 | type: 'TOGGLE_TODO', 188 | id 189 | }) 190 | } /> 191 | . 192 | . // FilterLink stuff 193 | . 194 |
      195 | ); 196 | } 197 | } 198 | ``` 199 | 200 | #### To Recap... 201 | The `TodoApp` component renders a `TodoList` and passes it a function that can dispatch an action. 202 | 203 | The `TodoList` component renders the `Todo` component, and passes an `onClick` prop which calls `onTodoClick()`. 204 | 205 | The `Todo` component uses the `onClick` prop it receives and binds it to the list item's `onClick`. This way when it's called, the `onTodoClick()` is called, which in turn dispatches the action, which in turn updates the visibile todos, since the action updates the store. 206 | 207 |

      208 | <- Prev 209 | Next -> 210 |

      211 | -------------------------------------------------------------------------------- /12-React_Todo_List_Example_Adding_a_Todo.md: -------------------------------------------------------------------------------- 1 | # 17. React Todo List Example (Adding a Todo) 2 | [Video Link](https://egghead.io/lessons/javascript-redux-react-todo-list-example-adding-a-todo) 3 | 4 | _WARNING: There's a lot going on here. It may be useful to watch the video as you go._ 5 | 6 | Now that we are managing our store and our actions, let's implement a view layer using React. 7 | 8 | Existing code: 9 | ```JavaScript 10 | const todo = (state, action) => { 11 | switch (action.type) { 12 | case 'ADD_TODO': 13 | return { 14 | id: action.id, 15 | text: action.text, 16 | completed: false 17 | }; 18 | case 'TOGGLE_TODO': 19 | if (state.id !== action.id) { 20 | return state; 21 | } 22 | 23 | return { 24 | ...state, 25 | completed: !state.completed 26 | }; 27 | default: 28 | return state; 29 | } 30 | }; 31 | 32 | const todos = (state = [], action) => { 33 | switch (action.type) { 34 | case 'ADD_TODO': 35 | return [ 36 | ...state, 37 | todo(undefined, action) 38 | ]; 39 | case 'TOGGLE_TODO': 40 | return state.map(t => 41 | todo(t, action) 42 | ); 43 | default: 44 | return state; 45 | } 46 | }; 47 | 48 | const visibilityFilter = ( 49 | state = 'SHOW_ALL', 50 | action 51 | ) => { 52 | switch (action.type) { 53 | case 'SET_VISIBILITY_FILTER': 54 | return action.filter; 55 | default: 56 | return state; 57 | } 58 | }; 59 | 60 | const { combineReducers } = Redux; 61 | const todoApp = combineReducers({ 62 | todos, 63 | visibilityFilter 64 | }); 65 | 66 | const { createStore } = Redux; 67 | const store = createStore(todoApp); 68 | ``` 69 | 70 | 71 | _Hopefully you already know a bit about React, JSX, props, etc._ 72 | 73 | **React-specific JS:** 74 | ```Javascript 75 | const { Component } = React; 76 | 77 | let nextTodoId = 0; 78 | class TodoApp extends Component { 79 | render() { 80 | return ( 81 |
      82 | 91 |
        92 | {this.props.todos.map(todo => 93 |
      • 94 | {todo.text} 95 |
      • 96 | )} 97 |
      98 |
      99 | ) 100 | }; 101 | } 102 | 103 | // See Section 8 for earlier `render()` example 104 | const render = () => { 105 | ReactDOM.render( 106 | // Render the TodoApp Component to the
      with id 'root' 107 | , 110 | document.getElementById('root') 111 | 112 | ) 113 | }; 114 | 115 | store.subscribe(render); 116 | render(); 117 | ``` 118 | 119 | HTML: 120 | ```HTML 121 | 122 | 123 | 124 | 125 | 126 | 127 |
      128 | 129 | 130 | ``` 131 | 132 | ### Now that all that code is written... 133 | With the code above, every time you click the "Add Todo" button, a new Todo item with the text "Test" is added to the bulleted list. 134 | 135 | Let's add an `` to our TodoApp component's return. We'll use React's callback `ref()` API. 136 | `ref()` is a function that gets the node corresponding to the ref that we'll save with the name `this.input`. 137 | 138 | From there, we can reference the value in `this.input` inside our button click handler, then reset the value after the `'ADD_TODO'` action has been dispatched. 139 | 140 | 141 | ```JavaScript 142 | . 143 | . 144 | . 145 | class TodoApp extends Component { 146 | render() { 147 | return ( 148 |
      149 | { 150 | this.input = node; 151 | }} /> 152 | 38 | 41 | store.dispatch({ 42 | type: 'TOGGLE_TODO', 43 | id 44 | }) 45 | } /> 46 | 47 |

      48 | Show: 49 | {' '} 50 | 54 | All 55 | 56 | {', '} 57 | 61 | Active 62 | 63 | {', '} 64 | 68 | Completed 69 | 70 |

      71 |
      72 | ); 73 | } 74 | } 75 | ``` 76 | 77 | #### Extracting the Input and the Button into `AddTodo` 78 | We will combine the input and the button into one new component called `AddTodo`. 79 | 80 | Functional components don't have instances, so instead of using `this`, we will use a variable called `input` that we will close over so we can write to it inside of the function. 81 | 82 | Since we want `AddTodo` to be a presentational component, we will have the button call an `onAddClick()` function with `input`'s value as its parameter. We also make `onAddClick` a prop so that the component that uses `AddTodo` can specify what happens when the "Add Todo" button is clicked. 83 | 84 | ```JavaScript 85 | const AddTodo = ({ 86 | onAddClick 87 | }) => { 88 | let input; 89 | 90 | return ( 91 |
      92 | { 93 | input = node; 94 | }} /> 95 | 101 |
      102 | ); 103 | }; 104 | ``` 105 | 106 | Now we need to update the `TodoApp` container component by replacing the `` and `