├── .gitignore ├── 01-react-redux-the-single-immutable-state-tree ├── app.css ├── index.html └── script.jsx ├── 02-react-redux-describing-state-changes-with-actions ├── app.css ├── index.html └── script.jsx ├── 03-react-redux-pure-and-impure-functions ├── app.css ├── index.html └── script.jsx ├── 04-javascript-redux-the-reducer-function ├── app.css ├── index.html └── script.jsx ├── 05-react-redux-writing-a-counter-reducer-with-tests ├── app.css ├── index.html └── script.jsx ├── 06-react-redux-store-methods-getstate-dispatch-and-subscribe ├── app.css ├── index.html └── script.jsx ├── 07-react-redux-implementing-store-from-scratch ├── app.css ├── index.html └── script.jsx ├── 08-react-redux-react-counter-example ├── app.css ├── index.html └── script.jsx ├── 09-react-redux-avoiding-array-mutations-with-concat-slice-and-spread ├── app.css ├── index.html └── script.jsx ├── 10-react-redux-avoiding-object-mutations-with-object-assign-and-spread ├── app.css ├── index.html └── script.jsx ├── 11-react-redux-writing-a-todo-list-reducer-adding-a-todo ├── app.css ├── index.html └── script.jsx ├── 12-react-redux-writing-a-todo-list-reducer-toggling-a-todo ├── app.css ├── index.html └── script.jsx ├── 13-react-redux-reducer-composition-with-arrays ├── app.css ├── index.html └── script.jsx ├── 14-react-redux-reducer-composition-with-objects ├── app.css ├── index.html └── script.jsx ├── 15-react-redux-reducer-composition-with-combinereducers ├── app.css ├── index.html └── script.jsx ├── 16-javascript-redux-implementing-combinereducers-from-scratch ├── app.css ├── index.html └── script.jsx ├── 17-react-redux-react-todo-list-example-adding-a-todo ├── app.css ├── index.html └── script.jsx ├── 18-react-redux-react-todo-list-example-toggling-a-todo ├── app.css ├── index.html └── script.jsx ├── 19-react-redux-react-todo-list-example-filtering-todos ├── app.css ├── index.html └── script.jsx ├── 20-react-redux-extracting-presentational-components-todo-todolist ├── app.css ├── index.html └── script.jsx ├── 21-react-redux-extracting-presentational-components-addtodo-footer-filterlink ├── app.css ├── index.html └── script.jsx ├── 22-react-redux-extracting-container-components-filterlink ├── app.css ├── index.html └── script.jsx ├── 23-react-redux-extracting-container-components-visibletodolist-addtodo ├── app.css ├── index.html └── script.jsx ├── 24-react-redux-passing-the-store-down-explicitly-via-props ├── app.css ├── index.html └── script.jsx ├── 25-react-redux-passing-the-store-down-implicitly-via-context ├── app.css ├── index.html └── script.jsx ├── 26-react-redux-passing-the-store-down-with-provider-from-react-redux ├── app.css ├── index.html └── script.jsx ├── 27-react-redux-generating-containers-with-connect-from-react-redux-visibletodolist ├── app.css ├── index.html └── script.jsx ├── 28-react-redux-generating-containers-with-connect-from-react-redux-addtodo ├── app.css ├── index.html └── script.jsx ├── 29-react-redux-generating-containers-with-connect-from-react-redux-footerlink ├── app.css ├── index.html └── script.jsx ├── 30-react-redux-extracting-action-creators ├── app.css ├── index.html └── script.jsx └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /01-react-redux-the-single-immutable-state-tree/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/01-react-redux-the-single-immutable-state-tree/app.css -------------------------------------------------------------------------------- /01-react-redux-the-single-immutable-state-tree/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /01-react-redux-the-single-immutable-state-tree/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * We will be explaining the code below 3 | * in the following lessons. For now, 4 | * feel free to click around and notice 5 | * how the current state tree is logged 6 | * to the console on every change. 7 | */ 8 | 9 | const ADD_TODO = 'ADD_TODO'; 10 | const TOGGLE_TODO = 'TOGGLE_TODO'; 11 | const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'; 12 | 13 | const Filters = { 14 | SHOW_ALL: 'SHOW_ALL', 15 | SHOW_COMPLETED: 'SHOW_COMPLETED', 16 | SHOW_ACTIVE: 'SHOW_ACTIVE' 17 | }; 18 | 19 | /* 20 | * Components 21 | */ 22 | 23 | class AddTodo extends React.Component { 24 | render() { 25 | return ( 26 |
27 | 28 | 31 |
32 | ); 33 | } 34 | 35 | handleClick(e) { 36 | const node = this.refs.input; 37 | const text = node.value.trim(); 38 | this.props.onAddClick(text); 39 | node.value = ''; 40 | } 41 | } 42 | 43 | const FilterLink = ({ isActive, name, onClick }) => { 44 | if (isActive) { 45 | return {name}; 46 | } 47 | 48 | return ( 49 | { e.preventDefault(); onClick(); }}> 50 | {name} 51 | 52 | ); 53 | }; 54 | 55 | const Footer = ({ filter, onFilterChange }) => ( 56 |

57 | Show: 58 | {' '} 59 | onFilterChange(Filters.SHOW_ALL)} /> 63 | {', '} 64 | onFilterChange(Filters.SHOW_COMPLETED)} /> 68 | {', '} 69 | onFilterChange(Filters.SHOW_ACTIVE)} /> 73 |

74 | ); 75 | 76 | const Todo = ({ onClick, completed, text }) => ( 77 |
  • 83 | {text} 84 |
  • 85 | ); 86 | 87 | 88 | const TodoList = ({ todos, onTodoClick }) => ( 89 | 96 | ); 97 | 98 | let nextTodoId = 0; 99 | const TodoApp = ({ dispatch, todos, visibilityFilter }) => { 100 | let visibleTodos = todos; 101 | 102 | switch (visibilityFilter) { 103 | case Filters.SHOW_COMPLETED: 104 | visibleTodos = todos.filter(todo => todo.completed); 105 | break; 106 | case Filters.SHOW_ACTIVE: 107 | visibleTodos = todos.filter(todo => !todo.completed); 108 | break; 109 | } 110 | 111 | return ( 112 |
    113 | 115 | dispatch({ type: ADD_TODO, text, id: nextTodoId++ }) 116 | } /> 117 | 120 | dispatch({ type: TOGGLE_TODO, id }) 121 | } /> 122 |
    128 | ); 129 | }; 130 | 131 | /* 132 | * Reducers 133 | */ 134 | 135 | const visibilityFilter = (state = Filters.SHOW_ALL, action) => { 136 | switch (action.type) { 137 | case SET_VISIBILITY_FILTER: 138 | return action.filter; 139 | default: 140 | return state; 141 | } 142 | } 143 | 144 | const todos = (state = [], action) => { 145 | switch (action.type) { 146 | case ADD_TODO: 147 | return [...state, { 148 | text: action.text, 149 | id: action.id, 150 | completed: false 151 | }]; 152 | case TOGGLE_TODO: 153 | return state.map(todo => 154 | todo.id === action.id ? 155 | Object.assign({}, todo, { completed: !todo.completed }) : 156 | todo 157 | ); 158 | default: 159 | return state; 160 | } 161 | } 162 | 163 | const todoApp = Redux.combineReducers({ 164 | visibilityFilter, 165 | todos 166 | }); 167 | 168 | /* 169 | * Go! 170 | */ 171 | 172 | const store = Redux.createStore(todoApp); 173 | const dispatch = (action) => { 174 | store.dispatch(action); 175 | console.log('----------------') || displayInPreview('----------------'); 176 | console.log('current state:') || displayInPreview('current state:'); 177 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 178 | } 179 | const render = () => { 180 | ReactDOM.render( 181 | , 185 | document.getElementById('root') 186 | ); 187 | } 188 | render(); 189 | store.subscribe(render); 190 | console.log('current state:') || displayInPreview('current state:'); 191 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 192 | 193 | // noprotect 194 | 195 | 196 | 197 | 198 | // display in plunker preview 199 | function displayInPreview(string) { 200 | var newDiv = document.createElement("div"); 201 | var newContent = document.createTextNode(string); 202 | newDiv.appendChild(newContent); 203 | document.body.appendChild(newDiv) 204 | } -------------------------------------------------------------------------------- /02-react-redux-describing-state-changes-with-actions/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/02-react-redux-describing-state-changes-with-actions/app.css -------------------------------------------------------------------------------- /02-react-redux-describing-state-changes-with-actions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /02-react-redux-describing-state-changes-with-actions/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * We will be explaining the code below 3 | * in the following lessons. For now, 4 | * feel free to click around and notice 5 | * how the dispatched action is logged 6 | * to the console on every change. 7 | */ 8 | 9 | const ADD_TODO = 'ADD_TODO'; 10 | const TOGGLE_TODO = 'TOGGLE_TODO'; 11 | const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'; 12 | 13 | const Filters = { 14 | SHOW_ALL: 'SHOW_ALL', 15 | SHOW_COMPLETED: 'SHOW_COMPLETED', 16 | SHOW_ACTIVE: 'SHOW_ACTIVE' 17 | }; 18 | 19 | /* 20 | * Components 21 | */ 22 | 23 | class AddTodo extends React.Component { 24 | render() { 25 | return ( 26 |
    27 | 28 | 31 |
    32 | ); 33 | } 34 | 35 | handleClick(e) { 36 | const node = this.refs.input; 37 | const text = node.value.trim(); 38 | this.props.onAddClick(text); 39 | node.value = ''; 40 | } 41 | } 42 | 43 | const FilterLink = ({ isActive, name, onClick }) => { 44 | if (isActive) { 45 | return {name}; 46 | } 47 | 48 | return ( 49 | { e.preventDefault(); onClick(); }}> 50 | {name} 51 | 52 | ); 53 | }; 54 | 55 | const Footer = ({ filter, onFilterChange }) => ( 56 |

    57 | Show: 58 | {' '} 59 | onFilterChange(Filters.SHOW_ALL)} /> 63 | {', '} 64 | onFilterChange(Filters.SHOW_COMPLETED)} /> 68 | {', '} 69 | onFilterChange(Filters.SHOW_ACTIVE)} /> 73 |

    74 | ); 75 | 76 | const Todo = ({ onClick, completed, text }) => ( 77 |
  • 83 | {text} 84 |
  • 85 | ); 86 | 87 | 88 | const TodoList = ({ todos, onTodoClick }) => ( 89 | 96 | ); 97 | 98 | let nextTodoId = 0; 99 | const TodoApp = ({ dispatch, todos, visibilityFilter }) => { 100 | let visibleTodos = todos; 101 | 102 | switch (visibilityFilter) { 103 | case Filters.SHOW_COMPLETED: 104 | visibleTodos = todos.filter(todo => todo.completed); 105 | break; 106 | case Filters.SHOW_ACTIVE: 107 | visibleTodos = todos.filter(todo => !todo.completed); 108 | break; 109 | } 110 | 111 | return ( 112 |
    113 | 115 | dispatch({ type: ADD_TODO, text, id: nextTodoId++ }) 116 | } /> 117 | 120 | dispatch({ type: TOGGLE_TODO, id }) 121 | } /> 122 |
    125 | dispatch({ type: SET_VISIBILITY_FILTER, filter }) 126 | } /> 127 |
    128 | ); 129 | }; 130 | 131 | /* 132 | * Reducers 133 | */ 134 | 135 | const visibilityFilter = (state = Filters.SHOW_ALL, action) => { 136 | switch (action.type) { 137 | case SET_VISIBILITY_FILTER: 138 | return action.filter; 139 | default: 140 | return state; 141 | } 142 | } 143 | 144 | const todos = (state = [], action) => { 145 | switch (action.type) { 146 | case ADD_TODO: 147 | return [...state, { 148 | text: action.text, 149 | id: action.id, 150 | completed: false 151 | }]; 152 | case TOGGLE_TODO: 153 | return state.map(todo => 154 | todo.id === action.id ? 155 | Object.assign({}, todo, { completed: !todo.completed }) : 156 | todo 157 | ); 158 | default: 159 | return state; 160 | } 161 | } 162 | 163 | const todoApp = Redux.combineReducers({ 164 | visibilityFilter, 165 | todos 166 | }); 167 | 168 | /* 169 | * Go! 170 | */ 171 | 172 | const store = Redux.createStore(todoApp); 173 | const dispatch = (action) => { 174 | console.log('----------------') || displayInPreview('----------------'); 175 | console.log('dispatching action:') || displayInPreview('dispatching action:'); 176 | console.log(action) || displayInPreview(action); 177 | store.dispatch(action); 178 | } 179 | const render = () => { 180 | ReactDOM.render( 181 | , 185 | document.getElementById('root') 186 | ); 187 | } 188 | render(); 189 | store.subscribe(render); 190 | // noprotect 191 | 192 | 193 | 194 | 195 | // display in plunker preview 196 | function displayInPreview(string) { 197 | var newDiv = document.createElement("div"); 198 | var newContent = document.createTextNode(string); 199 | newDiv.appendChild(newContent); 200 | document.body.appendChild(newDiv) 201 | } -------------------------------------------------------------------------------- /03-react-redux-pure-and-impure-functions/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/03-react-redux-pure-and-impure-functions/app.css -------------------------------------------------------------------------------- /03-react-redux-pure-and-impure-functions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /03-react-redux-pure-and-impure-functions/script.jsx: -------------------------------------------------------------------------------- 1 | // Pure functions 2 | function square(x) { 3 | return x * x; 4 | } 5 | function squareAll(items) { 6 | return items.map(square); 7 | } 8 | 9 | // Impure functions 10 | function square(x) { 11 | updateXInDatabase(x); 12 | return x * x; 13 | } 14 | function squareAll(items) { 15 | for (let i = 0; i < items.length; i++) { 16 | items[i] = square(items[i]); 17 | } 18 | } -------------------------------------------------------------------------------- /04-javascript-redux-the-reducer-function/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/04-javascript-redux-the-reducer-function/app.css -------------------------------------------------------------------------------- /04-javascript-redux-the-reducer-function/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /04-javascript-redux-the-reducer-function/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * We will be explaining the code below 3 | * in the following lessons. For now, 4 | * feel free to click around and notice 5 | * how the previous state tree, the 6 | * dispatched action, and the next 7 | * state tree are logged to the console 8 | * on every change. 9 | */ 10 | 11 | const ADD_TODO = 'ADD_TODO'; 12 | const TOGGLE_TODO = 'TOGGLE_TODO'; 13 | const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER'; 14 | 15 | const Filters = { 16 | SHOW_ALL: 'SHOW_ALL', 17 | SHOW_COMPLETED: 'SHOW_COMPLETED', 18 | SHOW_ACTIVE: 'SHOW_ACTIVE' 19 | }; 20 | 21 | /* 22 | * Components 23 | */ 24 | 25 | class AddTodo extends React.Component { 26 | render() { 27 | return ( 28 |
    29 | 30 | 33 |
    34 | ); 35 | } 36 | 37 | handleClick(e) { 38 | const node = this.refs.input; 39 | const text = node.value.trim(); 40 | this.props.onAddClick(text); 41 | node.value = ''; 42 | } 43 | } 44 | 45 | const FilterLink = ({ isActive, name, onClick }) => { 46 | if (isActive) { 47 | return {name}; 48 | } 49 | 50 | return ( 51 | { e.preventDefault(); onClick(); }}> 52 | {name} 53 | 54 | ); 55 | }; 56 | 57 | const Footer = ({ filter, onFilterChange }) => ( 58 |

    59 | Show: 60 | {' '} 61 | onFilterChange(Filters.SHOW_ALL)} /> 65 | {', '} 66 | onFilterChange(Filters.SHOW_COMPLETED)} /> 70 | {', '} 71 | onFilterChange(Filters.SHOW_ACTIVE)} /> 75 |

    76 | ); 77 | 78 | const Todo = ({ onClick, completed, text }) => ( 79 |
  • 85 | {text} 86 |
  • 87 | ); 88 | 89 | 90 | const TodoList = ({ todos, onTodoClick }) => ( 91 | 98 | ); 99 | 100 | let nextTodoId = 0; 101 | const TodoApp = ({ dispatch, todos, visibilityFilter }) => { 102 | let visibleTodos = todos; 103 | 104 | switch (visibilityFilter) { 105 | case Filters.SHOW_COMPLETED: 106 | visibleTodos = todos.filter(todo => todo.completed); 107 | break; 108 | case Filters.SHOW_ACTIVE: 109 | visibleTodos = todos.filter(todo => !todo.completed); 110 | break; 111 | } 112 | 113 | return ( 114 |
    115 | 117 | dispatch({ type: ADD_TODO, text, id: nextTodoId++ }) 118 | } /> 119 | 122 | dispatch({ type: TOGGLE_TODO, id }) 123 | } /> 124 |
    127 | dispatch({ type: SET_VISIBILITY_FILTER, filter }) 128 | } /> 129 |
    130 | ); 131 | }; 132 | 133 | /* 134 | * Reducers 135 | */ 136 | 137 | const visibilityFilter = (state = Filters.SHOW_ALL, action) => { 138 | switch (action.type) { 139 | case SET_VISIBILITY_FILTER: 140 | return action.filter; 141 | default: 142 | return state; 143 | } 144 | } 145 | 146 | const todos = (state = [], action) => { 147 | switch (action.type) { 148 | case ADD_TODO: 149 | return [...state, { 150 | text: action.text, 151 | id: action.id, 152 | completed: false 153 | }]; 154 | case TOGGLE_TODO: 155 | return state.map(todo => 156 | todo.id === action.id ? 157 | Object.assign({}, todo, { completed: !todo.completed }) : 158 | todo 159 | ); 160 | default: 161 | return state; 162 | } 163 | } 164 | 165 | const todoApp = Redux.combineReducers({ 166 | visibilityFilter, 167 | todos 168 | }); 169 | 170 | /* 171 | * Go! 172 | */ 173 | 174 | const store = Redux.createStore(todoApp); 175 | const dispatch = (action) => { 176 | console.log('----------------') || displayInPreview('----------------'); 177 | console.log('previous state:') || displayInPreview('previous state:'); 178 | console.log(store.getState()) || displayInPreview(store.getState()); 179 | console.log('dispatching action:') || displayInPreview('dispatching action:'); 180 | console.log(action) || displayInPreview(action); 181 | store.dispatch(action) || displayInPreview(action); 182 | console.log('next state:') || displayInPreview('next state:'); 183 | console.log(store.getState()) || displayInPreview(store.getState()); 184 | }; 185 | const render = () => { 186 | ReactDOM.render( 187 | , 191 | document.getElementById('root') 192 | ); 193 | } 194 | render(); 195 | store.subscribe(render); 196 | console.log('initial state:') || displayInPreview('initial state:'); 197 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 198 | // noprotect 199 | 200 | 201 | 202 | // display in plunker preview 203 | function displayInPreview(string) { 204 | var newDiv = document.createElement("div"); 205 | var newContent = document.createTextNode(string); 206 | newDiv.appendChild(newContent); 207 | document.body.appendChild(newDiv) 208 | } -------------------------------------------------------------------------------- /05-react-redux-writing-a-counter-reducer-with-tests/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/05-react-redux-writing-a-counter-reducer-with-tests/app.css -------------------------------------------------------------------------------- /05-react-redux-writing-a-counter-reducer-with-tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /05-react-redux-writing-a-counter-reducer-with-tests/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console tab 3 | * to see that the tests pass. 4 | */ 5 | 6 | const counter = (state = 0, action) => { 7 | switch (action.type) { 8 | case 'INCREMENT': 9 | return state + 1; 10 | case 'DECREMENT': 11 | return state - 1; 12 | default: 13 | return state; 14 | } 15 | } 16 | 17 | expect( 18 | counter(0, { type: 'INCREMENT' }) 19 | ).toEqual(1); 20 | 21 | expect( 22 | counter(1, { type: 'INCREMENT' }) 23 | ).toEqual(2); 24 | 25 | expect( 26 | counter(2, { type: 'DECREMENT' }) 27 | ).toEqual(1); 28 | 29 | expect( 30 | counter(1, { type: 'DECREMENT' }) 31 | ).toEqual(0); 32 | 33 | expect( 34 | counter(1, { type: 'SOMETHING_ELSE' }) 35 | ).toEqual(1); 36 | 37 | expect( 38 | counter(undefined, {}) 39 | ).toEqual(0); 40 | 41 | console.log('Tests passed!') || displayInPreview('Tests passed!'); 42 | 43 | 44 | 45 | // display in plunker preview 46 | function displayInPreview(string) { 47 | var newDiv = document.createElement("div"); 48 | var newContent = document.createTextNode(string); 49 | newDiv.appendChild(newContent); 50 | document.body.appendChild(newDiv) 51 | } -------------------------------------------------------------------------------- /06-react-redux-store-methods-getstate-dispatch-and-subscribe/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/06-react-redux-store-methods-getstate-dispatch-and-subscribe/app.css -------------------------------------------------------------------------------- /06-react-redux-store-methods-getstate-dispatch-and-subscribe/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /06-react-redux-store-methods-getstate-dispatch-and-subscribe/script.jsx: -------------------------------------------------------------------------------- 1 | const counter = (state = 0, action) => { 2 | switch (action.type) { 3 | case 'INCREMENT': 4 | return state + 1; 5 | case 'DECREMENT': 6 | return state - 1; 7 | default: 8 | return state; 9 | } 10 | } 11 | 12 | const { createStore } = Redux; 13 | const store = createStore(counter); 14 | 15 | const render = () => { 16 | document.body.innerText = store.getState(); 17 | }; 18 | 19 | store.subscribe(render); 20 | render(); 21 | 22 | document.addEventListener('click', () => { 23 | store.dispatch({ type: 'INCREMENT' }); 24 | }); -------------------------------------------------------------------------------- /07-react-redux-implementing-store-from-scratch/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/07-react-redux-implementing-store-from-scratch/app.css -------------------------------------------------------------------------------- /07-react-redux-implementing-store-from-scratch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /07-react-redux-implementing-store-from-scratch/script.jsx: -------------------------------------------------------------------------------- 1 | const counter = (state = 0, action) => { 2 | switch (action.type) { 3 | case 'INCREMENT': 4 | return state + 1; 5 | case 'DECREMENT': 6 | return state - 1; 7 | default: 8 | return state; 9 | } 10 | } 11 | 12 | const createStore = (reducer) => { 13 | let state; 14 | let listeners = []; 15 | 16 | const getState = () => state; 17 | 18 | const dispatch = (action) => { 19 | state = reducer(state, action); 20 | listeners.forEach(listener => listener()); 21 | }; 22 | 23 | const subscribe = (listener) => { 24 | listeners.push(listener); 25 | return () => { 26 | listeners = listeners.filter(l => l !== listener); 27 | }; 28 | }; 29 | 30 | dispatch({}); 31 | 32 | return { getState, dispatch, subscribe }; 33 | }; 34 | 35 | const store = createStore(counter); 36 | 37 | const render = () => { 38 | document.body.innerText = store.getState(); 39 | }; 40 | 41 | store.subscribe(render); 42 | render(); 43 | 44 | document.addEventListener('click', () => { 45 | store.dispatch({ type: 'INCREMENT' }); 46 | }); -------------------------------------------------------------------------------- /08-react-redux-react-counter-example/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/08-react-redux-react-counter-example/app.css -------------------------------------------------------------------------------- /08-react-redux-react-counter-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /08-react-redux-react-counter-example/script.jsx: -------------------------------------------------------------------------------- 1 | const counter = (state = 0, action) => { 2 | switch (action.type) { 3 | case 'INCREMENT': 4 | return state + 1; 5 | case 'DECREMENT': 6 | return state - 1; 7 | default: 8 | return state; 9 | } 10 | } 11 | 12 | const Counter = ({ 13 | value, 14 | onIncrement, 15 | onDecrement 16 | }) => ( 17 |
    18 |

    {value}

    19 | 20 | 21 |
    22 | ); 23 | 24 | const { createStore } = Redux; 25 | const store = createStore(counter); 26 | 27 | const render = () => { 28 | ReactDOM.render( 29 | 32 | store.dispatch({ 33 | type: 'INCREMENT' 34 | }) 35 | } 36 | onDecrement={() => 37 | store.dispatch({ 38 | type: 'DECREMENT' 39 | }) 40 | } 41 | />, 42 | document.getElementById('root') 43 | ); 44 | }; 45 | 46 | store.subscribe(render); 47 | render(); 48 | -------------------------------------------------------------------------------- /09-react-redux-avoiding-array-mutations-with-concat-slice-and-spread/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/09-react-redux-avoiding-array-mutations-with-concat-slice-and-spread/app.css -------------------------------------------------------------------------------- /09-react-redux-avoiding-array-mutations-with-concat-slice-and-spread/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /09-react-redux-avoiding-array-mutations-with-concat-slice-and-spread/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console to see 3 | * that all tests have passed. 4 | */ 5 | 6 | const addCounter = (list) => { 7 | return [...list, 0]; 8 | }; 9 | 10 | const removeCounter = (list, index) => { 11 | return [ 12 | ...list.slice(0, index), 13 | ...list.slice(index + 1) 14 | ]; 15 | }; 16 | 17 | const incrementCounter = (list, index) => { 18 | return [ 19 | ...list.slice(0, index), 20 | list[index] + 1, 21 | ...list.slice(index + 1) 22 | ]; 23 | }; 24 | 25 | const testAddCounter = () => { 26 | const listBefore = []; 27 | const listAfter = [0]; 28 | 29 | deepFreeze(listBefore); 30 | 31 | expect( 32 | addCounter(listBefore) 33 | ).toEqual(listAfter); 34 | }; 35 | 36 | const testRemoveCounter = () => { 37 | const listBefore = [0, 10, 20]; 38 | const listAfter = [0, 20]; 39 | 40 | deepFreeze(listBefore); 41 | 42 | expect( 43 | removeCounter(listBefore, 1) 44 | ).toEqual(listAfter); 45 | }; 46 | 47 | const testIncrementCounter = () => { 48 | const listBefore = [0, 10, 20]; 49 | const listAfter = [0, 11, 20]; 50 | 51 | deepFreeze(listBefore); 52 | 53 | expect( 54 | incrementCounter(listBefore, 1) 55 | ).toEqual(listAfter); 56 | }; 57 | 58 | testAddCounter(); 59 | testRemoveCounter(); 60 | testIncrementCounter(); 61 | 62 | console.log('All tests passed.') || displayInPreview('All tests passed.'); 63 | 64 | 65 | 66 | 67 | // display in plunker preview 68 | function displayInPreview(string) { 69 | var newDiv = document.createElement("div"); 70 | var newContent = document.createTextNode(string); 71 | newDiv.appendChild(newContent); 72 | document.body.appendChild(newDiv) 73 | } 74 | 75 | // Function exported from deep-freeze lib 76 | function deepFreeze (o) { 77 | if (o===Object(o)) { 78 | Object.isFrozen(o) || Object.freeze(o) 79 | Object.getOwnPropertyNames(o).forEach(function (prop) { 80 | prop==='constructor'||deepFreeze(o[prop]) 81 | }) 82 | } 83 | return o 84 | } -------------------------------------------------------------------------------- /10-react-redux-avoiding-object-mutations-with-object-assign-and-spread/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/10-react-redux-avoiding-object-mutations-with-object-assign-and-spread/app.css -------------------------------------------------------------------------------- /10-react-redux-avoiding-object-mutations-with-object-assign-and-spread/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /10-react-redux-avoiding-object-mutations-with-object-assign-and-spread/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console to see 3 | * that the tests have passed. 4 | */ 5 | 6 | const toggleTodo = (todo) => { 7 | return { 8 | ...todo, 9 | completed: !todo.completed 10 | }; 11 | }; 12 | 13 | const testToggleTodo = () => { 14 | const todoBefore = { 15 | id: 0, 16 | text: 'Learn Redux', 17 | completed: false 18 | }; 19 | const todoAfter = { 20 | id: 0, 21 | text: 'Learn Redux', 22 | completed: true 23 | }; 24 | 25 | deepFreeze(todoBefore); 26 | 27 | expect( 28 | toggleTodo(todoBefore) 29 | ).toEqual(todoAfter); 30 | }; 31 | 32 | testToggleTodo(); 33 | console.log('All tests passed.') || displayInPreview('All tests passed.'); 34 | 35 | 36 | 37 | // display in plunker preview 38 | function displayInPreview(string) { 39 | var newDiv = document.createElement("div"); 40 | var newContent = document.createTextNode(string); 41 | newDiv.appendChild(newContent); 42 | document.body.appendChild(newDiv) 43 | } 44 | 45 | // Function exported from deep-freeze lib 46 | function deepFreeze (o) { 47 | if (o===Object(o)) { 48 | Object.isFrozen(o) || Object.freeze(o) 49 | Object.getOwnPropertyNames(o).forEach(function (prop) { 50 | prop==='constructor'||deepFreeze(o[prop]) 51 | }) 52 | } 53 | return o 54 | } -------------------------------------------------------------------------------- /11-react-redux-writing-a-todo-list-reducer-adding-a-todo/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/11-react-redux-writing-a-todo-list-reducer-adding-a-todo/app.css -------------------------------------------------------------------------------- /11-react-redux-writing-a-todo-list-reducer-adding-a-todo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /11-react-redux-writing-a-todo-list-reducer-adding-a-todo/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console 3 | * to see that the tests pass. 4 | */ 5 | 6 | const todos = (state = [], action) => { 7 | switch (action.type) { 8 | case 'ADD_TODO': 9 | return [ 10 | ...state, 11 | { 12 | id: action.id, 13 | text: action.text, 14 | completed: false 15 | } 16 | ]; 17 | default: 18 | return state; 19 | } 20 | }; 21 | 22 | const testAddTodo = () => { 23 | const stateBefore = []; 24 | const action = { 25 | type: 'ADD_TODO', 26 | id: 0, 27 | text: 'Learn Redux' 28 | }; 29 | const stateAfter = [ 30 | { 31 | id: 0, 32 | text: 'Learn Redux', 33 | completed: false 34 | } 35 | ]; 36 | 37 | deepFreeze(stateBefore); 38 | deepFreeze(action); 39 | 40 | expect( 41 | todos(stateBefore, action) 42 | ).toEqual(stateAfter); 43 | }; 44 | 45 | testAddTodo(); 46 | console.log('All tests passed.') || displayInPreview('All tests passed.'); 47 | 48 | 49 | 50 | 51 | // display in plunker preview 52 | function displayInPreview(string) { 53 | var newDiv = document.createElement("div"); 54 | var newContent = document.createTextNode(string); 55 | newDiv.appendChild(newContent); 56 | document.body.appendChild(newDiv) 57 | } 58 | 59 | // Function exported from deep-freeze lib 60 | function deepFreeze (o) { 61 | if (o===Object(o)) { 62 | Object.isFrozen(o) || Object.freeze(o) 63 | Object.getOwnPropertyNames(o).forEach(function (prop) { 64 | prop==='constructor'||deepFreeze(o[prop]) 65 | }) 66 | } 67 | return o 68 | } -------------------------------------------------------------------------------- /12-react-redux-writing-a-todo-list-reducer-toggling-a-todo/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/12-react-redux-writing-a-todo-list-reducer-toggling-a-todo/app.css -------------------------------------------------------------------------------- /12-react-redux-writing-a-todo-list-reducer-toggling-a-todo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /12-react-redux-writing-a-todo-list-reducer-toggling-a-todo/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console 3 | * to see that the tests pass. 4 | */ 5 | 6 | const todos = (state = [], action) => { 7 | switch (action.type) { 8 | case 'ADD_TODO': 9 | return [ 10 | ...state, 11 | { 12 | id: action.id, 13 | text: action.text, 14 | completed: false 15 | } 16 | ]; 17 | case 'TOGGLE_TODO': 18 | return state.map(todo => { 19 | if (todo.id !== action.id) { 20 | return todo; 21 | } 22 | 23 | return { 24 | ...todo, 25 | completed: !todo.completed 26 | }; 27 | }); 28 | default: 29 | return state; 30 | } 31 | }; 32 | 33 | const testAddTodo = () => { 34 | const stateBefore = []; 35 | const action = { 36 | type: 'ADD_TODO', 37 | id: 0, 38 | text: 'Learn Redux' 39 | }; 40 | const stateAfter = [ 41 | { 42 | id: 0, 43 | text: 'Learn Redux', 44 | completed: false 45 | } 46 | ]; 47 | 48 | deepFreeze(stateBefore); 49 | deepFreeze(action); 50 | 51 | expect( 52 | todos(stateBefore, action) 53 | ).toEqual(stateAfter); 54 | }; 55 | 56 | const testToggleTodo = () => { 57 | const stateBefore = [ 58 | { 59 | id: 0, 60 | text: 'Learn Redux', 61 | completed: false 62 | }, 63 | { 64 | id: 1, 65 | text: 'Go shopping', 66 | completed: false 67 | } 68 | ]; 69 | const action = { 70 | type: 'TOGGLE_TODO', 71 | id: 1 72 | }; 73 | const stateAfter = [ 74 | { 75 | id: 0, 76 | text: 'Learn Redux', 77 | completed: false 78 | }, 79 | { 80 | id: 1, 81 | text: 'Go shopping', 82 | completed: true 83 | } 84 | ]; 85 | 86 | deepFreeze(stateBefore); 87 | deepFreeze(action); 88 | 89 | expect( 90 | todos(stateBefore, action) 91 | ).toEqual(stateAfter); 92 | }; 93 | 94 | 95 | testAddTodo(); 96 | testToggleTodo(); 97 | console.log('All tests passed.') || displayInPreview('All tests passed.'); 98 | 99 | 100 | 101 | 102 | // display in plunker preview 103 | function displayInPreview(string) { 104 | var newDiv = document.createElement("div"); 105 | var newContent = document.createTextNode(string); 106 | newDiv.appendChild(newContent); 107 | document.body.appendChild(newDiv) 108 | } 109 | 110 | // Function exported from deep-freeze lib 111 | function deepFreeze (o) { 112 | if (o===Object(o)) { 113 | Object.isFrozen(o) || Object.freeze(o) 114 | Object.getOwnPropertyNames(o).forEach(function (prop) { 115 | prop==='constructor'||deepFreeze(o[prop]) 116 | }) 117 | } 118 | return o 119 | } -------------------------------------------------------------------------------- /13-react-redux-reducer-composition-with-arrays/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/13-react-redux-reducer-composition-with-arrays/app.css -------------------------------------------------------------------------------- /13-react-redux-reducer-composition-with-arrays/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /13-react-redux-reducer-composition-with-arrays/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console 3 | * to see that the tests pass. 4 | */ 5 | 6 | const todo = (state, action) => { 7 | switch (action.type) { 8 | case 'ADD_TODO': 9 | return { 10 | id: action.id, 11 | text: action.text, 12 | completed: false 13 | }; 14 | case 'TOGGLE_TODO': 15 | if (state.id !== action.id) { 16 | return state; 17 | } 18 | 19 | return { 20 | ...state, 21 | completed: !state.completed 22 | }; 23 | default: 24 | return state; 25 | } 26 | }; 27 | 28 | const todos = (state = [], action) => { 29 | switch (action.type) { 30 | case 'ADD_TODO': 31 | return [ 32 | ...state, 33 | todo(undefined, action) 34 | ]; 35 | case 'TOGGLE_TODO': 36 | return state.map(t => todo(t, action)); 37 | default: 38 | return state; 39 | } 40 | }; 41 | 42 | const testAddTodo = () => { 43 | const stateBefore = []; 44 | const action = { 45 | type: 'ADD_TODO', 46 | id: 0, 47 | text: 'Learn Redux' 48 | }; 49 | const stateAfter = [ 50 | { 51 | id: 0, 52 | text: 'Learn Redux', 53 | completed: false 54 | } 55 | ]; 56 | 57 | deepFreeze(stateBefore); 58 | deepFreeze(action); 59 | 60 | expect( 61 | todos(stateBefore, action) 62 | ).toEqual(stateAfter); 63 | }; 64 | 65 | const testToggleTodo = () => { 66 | const stateBefore = [ 67 | { 68 | id: 0, 69 | text: 'Learn Redux', 70 | completed: false 71 | }, 72 | { 73 | id: 1, 74 | text: 'Go shopping', 75 | completed: false 76 | } 77 | ]; 78 | const action = { 79 | type: 'TOGGLE_TODO', 80 | id: 1 81 | }; 82 | const stateAfter = [ 83 | { 84 | id: 0, 85 | text: 'Learn Redux', 86 | completed: false 87 | }, 88 | { 89 | id: 1, 90 | text: 'Go shopping', 91 | completed: true 92 | } 93 | ]; 94 | 95 | deepFreeze(stateBefore); 96 | deepFreeze(action); 97 | 98 | expect( 99 | todos(stateBefore, action) 100 | ).toEqual(stateAfter); 101 | }; 102 | 103 | 104 | testAddTodo(); 105 | testToggleTodo(); 106 | console.log('All tests passed.') || displayInPreview('All tests passed.'); 107 | 108 | 109 | 110 | // display in plunker preview 111 | function displayInPreview(string) { 112 | var newDiv = document.createElement("div"); 113 | var newContent = document.createTextNode(string); 114 | newDiv.appendChild(newContent); 115 | document.body.appendChild(newDiv) 116 | } 117 | 118 | // Function exported from deep-freeze lib 119 | function deepFreeze (o) { 120 | if (o===Object(o)) { 121 | Object.isFrozen(o) || Object.freeze(o) 122 | Object.getOwnPropertyNames(o).forEach(function (prop) { 123 | prop==='constructor'||deepFreeze(o[prop]) 124 | }) 125 | } 126 | return o 127 | } -------------------------------------------------------------------------------- /14-react-redux-reducer-composition-with-objects/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/14-react-redux-reducer-composition-with-objects/app.css -------------------------------------------------------------------------------- /14-react-redux-reducer-composition-with-objects/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

    Open console to view results!

    15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /14-react-redux-reducer-composition-with-objects/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console 3 | * to see the state log. 4 | */ 5 | 6 | const todo = (state, action) => { 7 | switch (action.type) { 8 | case 'ADD_TODO': 9 | return { 10 | id: action.id, 11 | text: action.text, 12 | completed: false 13 | }; 14 | case 'TOGGLE_TODO': 15 | if (state.id !== action.id) { 16 | return state; 17 | } 18 | 19 | return { 20 | ...state, 21 | completed: !state.completed 22 | }; 23 | default: 24 | return state; 25 | } 26 | }; 27 | 28 | const todos = (state = [], action) => { 29 | switch (action.type) { 30 | case 'ADD_TODO': 31 | return [ 32 | ...state, 33 | todo(undefined, action) 34 | ]; 35 | case 'TOGGLE_TODO': 36 | return state.map(t => todo(t, action)); 37 | default: 38 | return state; 39 | } 40 | }; 41 | 42 | const visibilityFilter = ( 43 | state = 'SHOW_ALL', 44 | action 45 | ) => { 46 | switch (action.type) { 47 | case 'SET_VISIBILITY_FILTER': 48 | return action.filter; 49 | default: 50 | return state; 51 | } 52 | }; 53 | 54 | const todoApp = (state = {}, action) => { 55 | return { 56 | todos: todos( 57 | state.todos, 58 | action 59 | ), 60 | visibilityFilter: visibilityFilter( 61 | state.visibilityFilter, 62 | action 63 | ) 64 | }; 65 | }; 66 | 67 | const { createStore } = Redux; 68 | const store = createStore(todoApp); 69 | 70 | console.log('Initial state:'); 71 | console.log(store.getState()); 72 | console.log('--------------'); 73 | 74 | console.log('Dispatching ADD_TODO.') 75 | store.dispatch({ 76 | type: 'ADD_TODO', 77 | id: 0, 78 | text: 'Learn Redux' 79 | }); 80 | console.log('Current state:'); 81 | console.log(store.getState()); 82 | console.log('--------------'); 83 | 84 | console.log('Dispatching ADD_TODO.'); 85 | store.dispatch({ 86 | type: 'ADD_TODO', 87 | id: 1, 88 | text: 'Go shopping' 89 | }); 90 | console.log('Current state:'); 91 | console.log(store.getState()); 92 | console.log('--------------'); 93 | 94 | console.log('Dispatching TOGGLE_TODO.'); 95 | store.dispatch({ 96 | type: 'TOGGLE_TODO', 97 | id: 0 98 | }); 99 | console.log('Current state:'); 100 | console.log(store.getState()); 101 | console.log('--------------'); 102 | 103 | console.log('Dispatching SET_VISIBILITY_FILTER'); 104 | store.dispatch({ 105 | type: 'SET_VISIBILITY_FILTER', 106 | filter: 'SHOW_COMPLETED' 107 | }); 108 | console.log('Current state:'); 109 | console.log(store.getState()); 110 | console.log('--------------'); 111 | 112 | 113 | 114 | // display in plunker preview 115 | function displayInPreview(string) { 116 | var newDiv = document.createElement("div"); 117 | var newContent = document.createTextNode(string); 118 | newDiv.appendChild(newContent); 119 | document.body.appendChild(newDiv) 120 | } 121 | -------------------------------------------------------------------------------- /15-react-redux-reducer-composition-with-combinereducers/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/15-react-redux-reducer-composition-with-combinereducers/app.css -------------------------------------------------------------------------------- /15-react-redux-reducer-composition-with-combinereducers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

    Open browser console to view full results!

    15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /15-react-redux-reducer-composition-with-combinereducers/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console 3 | * to see the state log. 4 | */ 5 | 6 | const todo = (state, action) => { 7 | switch (action.type) { 8 | case 'ADD_TODO': 9 | return { 10 | id: action.id, 11 | text: action.text, 12 | completed: false 13 | }; 14 | case 'TOGGLE_TODO': 15 | if (state.id !== action.id) { 16 | return state; 17 | } 18 | 19 | return { 20 | ...state, 21 | completed: !state.completed 22 | }; 23 | default: 24 | return state; 25 | } 26 | }; 27 | 28 | const todos = (state = [], action) => { 29 | switch (action.type) { 30 | case 'ADD_TODO': 31 | return [ 32 | ...state, 33 | todo(undefined, action) 34 | ]; 35 | case 'TOGGLE_TODO': 36 | return state.map(t => todo(t, action)); 37 | default: 38 | return state; 39 | } 40 | }; 41 | 42 | const visibilityFilter = ( 43 | state = 'SHOW_ALL', 44 | action 45 | ) => { 46 | switch (action.type) { 47 | case 'SET_VISIBILITY_FILTER': 48 | return action.filter; 49 | default: 50 | return state; 51 | } 52 | }; 53 | 54 | const { combineReducers } = Redux; 55 | const todoApp = combineReducers({ 56 | todos, 57 | visibilityFilter 58 | }); 59 | 60 | const { createStore } = Redux; 61 | const store = createStore(todoApp); 62 | 63 | console.log('Initial state:') || displayInPreview('Initial state:'); 64 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 65 | console.log('--------------') || displayInPreview('--------------'); 66 | 67 | 68 | console.log('Dispatching ADD_TODO.') || displayInPreview('Dispatching ADD_TODO.'); 69 | store.dispatch({ 70 | type: 'ADD_TODO', 71 | id: 0, 72 | text: 'Learn Redux' 73 | }); 74 | console.log('Initial state:') || displayInPreview('Initial state:'); 75 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 76 | console.log('--------------') || displayInPreview('--------------'); 77 | 78 | 79 | console.log('Dispatching ADD_TODO.') || displayInPreview('Dispatching ADD_TODO.'); 80 | store.dispatch({ 81 | type: 'ADD_TODO', 82 | id: 1, 83 | text: 'Go shopping' 84 | }); 85 | console.log('Initial state:') || displayInPreview('Initial state:'); 86 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 87 | console.log('--------------') || displayInPreview('--------------'); 88 | 89 | 90 | console.log('Dispatching TOGGLE_TODO.') || displayInPreview('Dispatching TOGGLE_TODO.'); 91 | store.dispatch({ 92 | type: 'TOGGLE_TODO', 93 | id: 0 94 | }); 95 | console.log('Initial state:') || displayInPreview('Initial state:'); 96 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 97 | console.log('--------------') || displayInPreview('--------------'); 98 | 99 | 100 | console.log('Dispatching SET_VISIBILITY_FILTER') || displayInPreview('Dispatching SET_VISIBILITY_FILTER'); 101 | store.dispatch({ 102 | type: 'SET_VISIBILITY_FILTER', 103 | filter: 'SHOW_COMPLETED' 104 | }); 105 | console.log('Initial state:') || displayInPreview('Initial state:'); 106 | console.log(store.getState()) || displayInPreview(store.getState().visibilityFilter); 107 | console.log('--------------') || displayInPreview('--------------'); 108 | 109 | 110 | 111 | // display in plunker preview 112 | function displayInPreview(string) { 113 | var newDiv = document.createElement("div"); 114 | var newContent = document.createTextNode(string); 115 | newDiv.appendChild(newContent); 116 | document.body.appendChild(newDiv) 117 | } -------------------------------------------------------------------------------- /16-javascript-redux-implementing-combinereducers-from-scratch/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/16-javascript-redux-implementing-combinereducers-from-scratch/app.css -------------------------------------------------------------------------------- /16-javascript-redux-implementing-combinereducers-from-scratch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JS Bin 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /16-javascript-redux-implementing-combinereducers-from-scratch/script.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Open the console 3 | * to see the state log. 4 | */ 5 | 6 | const todo = (state, action) => { 7 | switch (action.type) { 8 | case 'ADD_TODO': 9 | return { 10 | id: action.id, 11 | text: action.text, 12 | completed: false 13 | }; 14 | case 'TOGGLE_TODO': 15 | if (state.id !== action.id) { 16 | return state; 17 | } 18 | 19 | return { 20 | ...state, 21 | completed: !state.completed 22 | }; 23 | default: 24 | return state; 25 | } 26 | }; 27 | 28 | const todos = (state = [], action) => { 29 | switch (action.type) { 30 | case 'ADD_TODO': 31 | return [ 32 | ...state, 33 | todo(undefined, action) 34 | ]; 35 | case 'TOGGLE_TODO': 36 | return state.map(t => todo(t, action)); 37 | default: 38 | return state; 39 | } 40 | }; 41 | 42 | const visibilityFilter = ( 43 | state = 'SHOW_ALL', 44 | action 45 | ) => { 46 | switch (action.type) { 47 | case 'SET_VISIBILITY_FILTER': 48 | return action.filter; 49 | default: 50 | return state; 51 | } 52 | }; 53 | 54 | const combineReducers = (reducers) => { 55 | return (state = {}, action) => { 56 | return Object.keys(reducers).reduce( 57 | (nextState, key) => { 58 | nextState[key] = reducers[key]( 59 | state[key], 60 | action 61 | ); 62 | return nextState; 63 | }, 64 | {} 65 | ); 66 | }; 67 | }; 68 | 69 | const todoApp = combineReducers({ 70 | todos, 71 | visibilityFilter 72 | }); 73 | 74 | const { createStore } = Redux; 75 | const store = createStore(todoApp); 76 | 77 | console.log('Initial state:'); 78 | console.log(store.getState()); 79 | console.log('--------------'); 80 | 81 | console.log('Dispatching ADD_TODO.'); 82 | store.dispatch({ 83 | type: 'ADD_TODO', 84 | id: 0, 85 | text: 'Learn Redux' 86 | }); 87 | console.log('Current state:'); 88 | console.log(store.getState()); 89 | console.log('--------------'); 90 | 91 | console.log('Dispatching ADD_TODO.'); 92 | store.dispatch({ 93 | type: 'ADD_TODO', 94 | id: 1, 95 | text: 'Go shopping' 96 | }); 97 | console.log('Current state:'); 98 | console.log(store.getState()); 99 | console.log('--------------'); 100 | 101 | console.log('Dispatching TOGGLE_TODO.'); 102 | store.dispatch({ 103 | type: 'TOGGLE_TODO', 104 | id: 0 105 | }); 106 | console.log('Current state:'); 107 | console.log(store.getState()); 108 | console.log('--------------'); 109 | 110 | console.log('Dispatching SET_VISIBILITY_FILTER'); 111 | store.dispatch({ 112 | type: 'SET_VISIBILITY_FILTER', 113 | filter: 'SHOW_COMPLETED' 114 | }); 115 | console.log('Current state:'); 116 | console.log(store.getState()); 117 | console.log('--------------'); -------------------------------------------------------------------------------- /17-react-redux-react-todo-list-example-adding-a-todo/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/17-react-redux-react-todo-list-example-adding-a-todo/app.css -------------------------------------------------------------------------------- /17-react-redux-react-todo-list-example-adding-a-todo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /17-react-redux-react-todo-list-example-adding-a-todo/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { createStore } = Redux; 58 | const store = createStore(todoApp); 59 | 60 | const { Component } = React; 61 | 62 | let nextTodoId = 0; 63 | class TodoApp extends Component { 64 | render() { 65 | return ( 66 |
    67 | { 68 | this.input = node; 69 | }} /> 70 | 80 |
      81 | {this.props.todos.map(todo => 82 |
    • 83 | {todo.text} 84 |
    • 85 | )} 86 |
    87 |
    88 | ); 89 | } 90 | } 91 | 92 | const render = () => { 93 | ReactDOM.render( 94 | , 97 | document.getElementById('root') 98 | ); 99 | }; 100 | 101 | store.subscribe(render); 102 | render(); -------------------------------------------------------------------------------- /18-react-redux-react-todo-list-example-toggling-a-todo/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/18-react-redux-react-todo-list-example-toggling-a-todo/app.css -------------------------------------------------------------------------------- /18-react-redux-react-todo-list-example-toggling-a-todo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /18-react-redux-react-todo-list-example-toggling-a-todo/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { createStore } = Redux; 58 | const store = createStore(todoApp); 59 | 60 | const { Component } = React; 61 | 62 | let nextTodoId = 0; 63 | class TodoApp extends Component { 64 | render() { 65 | return ( 66 |
    67 | { 68 | this.input = node; 69 | }} /> 70 | 80 |
      81 | {this.props.todos.map(todo => 82 |
    • { 84 | store.dispatch({ 85 | type: 'TOGGLE_TODO', 86 | id: todo.id 87 | }); 88 | }} 89 | style={{ 90 | textDecoration: 91 | todo.completed ? 92 | 'line-through' : 93 | 'none' 94 | }}> 95 | {todo.text} 96 |
    • 97 | )} 98 |
    99 |
    100 | ); 101 | } 102 | } 103 | 104 | const render = () => { 105 | ReactDOM.render( 106 | , 109 | document.getElementById('root') 110 | ); 111 | }; 112 | 113 | store.subscribe(render); 114 | render(); -------------------------------------------------------------------------------- /19-react-redux-react-todo-list-example-filtering-todos/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/19-react-redux-react-todo-list-example-filtering-todos/app.css -------------------------------------------------------------------------------- /19-react-redux-react-todo-list-example-filtering-todos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /19-react-redux-react-todo-list-example-filtering-todos/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { createStore } = Redux; 58 | const store = createStore(todoApp); 59 | 60 | const { Component } = React; 61 | 62 | const FilterLink = ({ 63 | filter, 64 | currentFilter, 65 | children 66 | }) => { 67 | if (filter === currentFilter) { 68 | return {children}; 69 | } 70 | 71 | return ( 72 | { 74 | e.preventDefault(); 75 | store.dispatch({ 76 | type: 'SET_VISIBILITY_FILTER', 77 | filter 78 | }); 79 | }} 80 | > 81 | {children} 82 | 83 | ); 84 | }; 85 | 86 | const getVisibleTodos = ( 87 | todos, 88 | filter 89 | ) => { 90 | switch (filter) { 91 | case 'SHOW_ALL': 92 | return todos; 93 | case 'SHOW_COMPLETED': 94 | return todos.filter( 95 | t => t.completed 96 | ); 97 | case 'SHOW_ACTIVE': 98 | return todos.filter( 99 | t => !t.completed 100 | ); 101 | } 102 | } 103 | 104 | let nextTodoId = 0; 105 | class TodoApp extends Component { 106 | render() { 107 | const { 108 | todos, 109 | visibilityFilter 110 | } = this.props; 111 | const visibleTodos = getVisibleTodos( 112 | todos, 113 | visibilityFilter 114 | ); 115 | return ( 116 |
    117 | { 118 | this.input = node; 119 | }} /> 120 | 130 |
      131 | {visibleTodos.map(todo => 132 |
    • { 134 | store.dispatch({ 135 | type: 'TOGGLE_TODO', 136 | id: todo.id 137 | }); 138 | }} 139 | style={{ 140 | textDecoration: 141 | todo.completed ? 142 | 'line-through' : 143 | 'none' 144 | }}> 145 | {todo.text} 146 |
    • 147 | )} 148 |
    149 |

    150 | Show: 151 | {' '} 152 | 156 | All 157 | 158 | {', '} 159 | 163 | Active 164 | 165 | {', '} 166 | 170 | Completed 171 | 172 |

    173 |
    174 | ); 175 | } 176 | } 177 | 178 | const render = () => { 179 | ReactDOM.render( 180 | , 183 | document.getElementById('root') 184 | ); 185 | }; 186 | 187 | store.subscribe(render); 188 | render(); -------------------------------------------------------------------------------- /20-react-redux-extracting-presentational-components-todo-todolist/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/20-react-redux-extracting-presentational-components-todo-todolist/app.css -------------------------------------------------------------------------------- /20-react-redux-extracting-presentational-components-todo-todolist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /20-react-redux-extracting-presentational-components-todo-todolist/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { createStore } = Redux; 58 | const store = createStore(todoApp); 59 | 60 | const { Component } = React; 61 | 62 | const FilterLink = ({ 63 | filter, 64 | currentFilter, 65 | children 66 | }) => { 67 | if (filter === currentFilter) { 68 | return {children}; 69 | } 70 | 71 | return ( 72 | { 74 | e.preventDefault(); 75 | store.dispatch({ 76 | type: 'SET_VISIBILITY_FILTER', 77 | filter 78 | }); 79 | }} 80 | > 81 | {children} 82 | 83 | ); 84 | }; 85 | 86 | const Todo = ({ 87 | onClick, 88 | completed, 89 | text 90 | }) => ( 91 |
  • 100 | {text} 101 |
  • 102 | ); 103 | 104 | const TodoList = ({ 105 | todos, 106 | onTodoClick 107 | }) => ( 108 |
      109 | {todos.map(todo => 110 | onTodoClick(todo.id)} 114 | /> 115 | )} 116 |
    117 | ); 118 | 119 | const getVisibleTodos = ( 120 | todos, 121 | filter 122 | ) => { 123 | switch (filter) { 124 | case 'SHOW_ALL': 125 | return todos; 126 | case 'SHOW_COMPLETED': 127 | return todos.filter( 128 | t => t.completed 129 | ); 130 | case 'SHOW_ACTIVE': 131 | return todos.filter( 132 | t => !t.completed 133 | ); 134 | } 135 | } 136 | 137 | let nextTodoId = 0; 138 | class TodoApp extends Component { 139 | render() { 140 | const { 141 | todos, 142 | visibilityFilter 143 | } = this.props; 144 | const visibleTodos = getVisibleTodos( 145 | todos, 146 | visibilityFilter 147 | ); 148 | return ( 149 |
    150 | { 151 | this.input = node; 152 | }} /> 153 | 163 | 166 | store.dispatch({ 167 | type: 'TOGGLE_TODO', 168 | id 169 | }) 170 | } 171 | /> 172 |

    173 | Show: 174 | {' '} 175 | 179 | All 180 | 181 | {', '} 182 | 186 | Active 187 | 188 | {', '} 189 | 193 | Completed 194 | 195 |

    196 |
    197 | ); 198 | } 199 | } 200 | 201 | const render = () => { 202 | ReactDOM.render( 203 | , 206 | document.getElementById('root') 207 | ); 208 | }; 209 | 210 | store.subscribe(render); 211 | render(); -------------------------------------------------------------------------------- /21-react-redux-extracting-presentational-components-addtodo-footer-filterlink/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/21-react-redux-extracting-presentational-components-addtodo-footer-filterlink/app.css -------------------------------------------------------------------------------- /21-react-redux-extracting-presentational-components-addtodo-footer-filterlink/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /21-react-redux-extracting-presentational-components-addtodo-footer-filterlink/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | return { 14 | ...state, 15 | completed: !state.completed 16 | }; 17 | default: 18 | return state; 19 | } 20 | }; 21 | 22 | const todos = (state = [], action) => { 23 | switch (action.type) { 24 | case 'ADD_TODO': 25 | return [ 26 | ...state, 27 | todo(undefined, action) 28 | ]; 29 | case 'TOGGLE_TODO': 30 | return state.map(t => 31 | todo(t, action) 32 | ); 33 | default: 34 | return state; 35 | } 36 | }; 37 | 38 | const visibilityFilter = ( 39 | state = 'SHOW_ALL', 40 | action 41 | ) => { 42 | switch (action.type) { 43 | case 'SET_VISIBILITY_FILTER': 44 | return action.filter; 45 | default: 46 | return state; 47 | } 48 | }; 49 | 50 | const { combineReducers } = Redux; 51 | const todoApp = combineReducers({ 52 | todos, 53 | visibilityFilter 54 | }); 55 | 56 | const { createStore } = Redux; 57 | const store = createStore(todoApp); 58 | 59 | const { Component } = React; 60 | 61 | const FilterLink = ({ 62 | filter, 63 | currentFilter, 64 | children, 65 | onClick 66 | }) => { 67 | if (filter === currentFilter) { 68 | return {children}; 69 | } 70 | 71 | return ( 72 | { 74 | e.preventDefault(); 75 | onClick(filter); 76 | }} 77 | > 78 | {children} 79 | 80 | ); 81 | }; 82 | 83 | const Footer = ({ 84 | visibilityFilter, 85 | onFilterClick 86 | }) => ( 87 |

    88 | Show: 89 | {' '} 90 | 95 | All 96 | 97 | {', '} 98 | 103 | Active 104 | 105 | {', '} 106 | 111 | Completed 112 | 113 |

    114 | ); 115 | 116 | const Todo = ({ 117 | onClick, 118 | completed, 119 | text 120 | }) => ( 121 |
  • 130 | {text} 131 |
  • 132 | ); 133 | 134 | const TodoList = ({ 135 | todos, 136 | onTodoClick 137 | }) => ( 138 |
      139 | {todos.map(todo => 140 | onTodoClick(todo.id)} 144 | /> 145 | )} 146 |
    147 | ); 148 | 149 | const AddTodo = ({ 150 | onAddClick 151 | }) => { 152 | let input; 153 | 154 | return ( 155 |
    156 | { 157 | input = node; 158 | }} /> 159 | 165 |
    166 | ); 167 | }; 168 | 169 | const getVisibleTodos = ( 170 | todos, 171 | filter 172 | ) => { 173 | switch (filter) { 174 | case 'SHOW_ALL': 175 | return todos; 176 | case 'SHOW_COMPLETED': 177 | return todos.filter( 178 | t => t.completed 179 | ); 180 | case 'SHOW_ACTIVE': 181 | return todos.filter( 182 | t => !t.completed 183 | ); 184 | } 185 | } 186 | 187 | let nextTodoId = 0; 188 | const TodoApp = ({ 189 | todos, 190 | visibilityFilter 191 | }) => ( 192 |
    193 | 195 | store.dispatch({ 196 | type: 'ADD_TODO', 197 | id: nextTodoId++, 198 | text 199 | }) 200 | } 201 | /> 202 | 210 | store.dispatch({ 211 | type: 'TOGGLE_TODO', 212 | id 213 | }) 214 | } 215 | /> 216 |
    219 | store.dispatch({ 220 | type: 'SET_VISIBILITY_FILTER', 221 | filter 222 | }) 223 | } 224 | /> 225 |
    226 | ); 227 | 228 | const render = () => { 229 | ReactDOM.render( 230 | , 233 | document.getElementById('root') 234 | ); 235 | }; 236 | 237 | store.subscribe(render); 238 | render(); -------------------------------------------------------------------------------- /22-react-redux-extracting-container-components-filterlink/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/22-react-redux-extracting-container-components-filterlink/app.css -------------------------------------------------------------------------------- /22-react-redux-extracting-container-components-filterlink/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /22-react-redux-extracting-container-components-filterlink/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { createStore } = Redux; 58 | const store = createStore(todoApp); 59 | 60 | const { Component } = React; 61 | 62 | const Link = ({ 63 | active, 64 | children, 65 | onClick 66 | }) => { 67 | if (active) { 68 | return {children}; 69 | } 70 | 71 | return ( 72 | { 74 | e.preventDefault(); 75 | onClick(); 76 | }} 77 | > 78 | {children} 79 | 80 | ); 81 | }; 82 | 83 | class FilterLink extends Component { 84 | componentDidMount() { 85 | this.unsubscribe = store.subscribe(() => 86 | this.forceUpdate() 87 | ); 88 | } 89 | 90 | componentWillUnmount() { 91 | this.unsubscribe(); 92 | } 93 | 94 | render() { 95 | const props = this.props; 96 | const state = store.getState(); 97 | 98 | return ( 99 | 105 | store.dispatch({ 106 | type: 'SET_VISIBILITY_FILTER', 107 | filter: props.filter 108 | }) 109 | } 110 | > 111 | {props.children} 112 | 113 | ); 114 | } 115 | } 116 | 117 | const Footer = () => ( 118 |

    119 | Show: 120 | {' '} 121 | 124 | All 125 | 126 | {', '} 127 | 130 | Active 131 | 132 | {', '} 133 | 136 | Completed 137 | 138 |

    139 | ); 140 | 141 | const Todo = ({ 142 | onClick, 143 | completed, 144 | text 145 | }) => ( 146 |
  • 155 | {text} 156 |
  • 157 | ); 158 | 159 | const TodoList = ({ 160 | todos, 161 | onTodoClick 162 | }) => ( 163 |
      164 | {todos.map(todo => 165 | onTodoClick(todo.id)} 169 | /> 170 | )} 171 |
    172 | ); 173 | 174 | const AddTodo = ({ 175 | onAddClick 176 | }) => { 177 | let input; 178 | 179 | return ( 180 |
    181 | { 182 | input = node; 183 | }} /> 184 | 190 |
    191 | ); 192 | }; 193 | 194 | const getVisibleTodos = ( 195 | todos, 196 | filter 197 | ) => { 198 | switch (filter) { 199 | case 'SHOW_ALL': 200 | return todos; 201 | case 'SHOW_COMPLETED': 202 | return todos.filter( 203 | t => t.completed 204 | ); 205 | case 'SHOW_ACTIVE': 206 | return todos.filter( 207 | t => !t.completed 208 | ); 209 | } 210 | } 211 | 212 | let nextTodoId = 0; 213 | const TodoApp = ({ 214 | todos, 215 | visibilityFilter 216 | }) => ( 217 |
    218 | 220 | store.dispatch({ 221 | type: 'ADD_TODO', 222 | id: nextTodoId++, 223 | text 224 | }) 225 | } 226 | /> 227 | 235 | store.dispatch({ 236 | type: 'TOGGLE_TODO', 237 | id 238 | }) 239 | } 240 | /> 241 |
    242 |
    243 | ); 244 | 245 | const render = () => { 246 | ReactDOM.render( 247 | , 250 | document.getElementById('root') 251 | ); 252 | }; 253 | 254 | store.subscribe(render); 255 | render(); -------------------------------------------------------------------------------- /23-react-redux-extracting-container-components-visibletodolist-addtodo/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/23-react-redux-extracting-container-components-visibletodolist-addtodo/app.css -------------------------------------------------------------------------------- /23-react-redux-extracting-container-components-visibletodolist-addtodo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /23-react-redux-extracting-container-components-visibletodolist-addtodo/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { createStore } = Redux; 58 | const store = createStore(todoApp); 59 | 60 | const { Component } = React; 61 | 62 | const Link = ({ 63 | active, 64 | children, 65 | onClick 66 | }) => { 67 | if (active) { 68 | return {children}; 69 | } 70 | 71 | return ( 72 | { 74 | e.preventDefault(); 75 | onClick(); 76 | }} 77 | > 78 | {children} 79 | 80 | ); 81 | }; 82 | 83 | class FilterLink extends Component { 84 | componentDidMount() { 85 | this.unsubscribe = store.subscribe(() => 86 | this.forceUpdate() 87 | ); 88 | } 89 | 90 | componentWillUnmount() { 91 | this.unsubscribe(); 92 | } 93 | 94 | render() { 95 | const props = this.props; 96 | const state = store.getState(); 97 | 98 | return ( 99 | 105 | store.dispatch({ 106 | type: 'SET_VISIBILITY_FILTER', 107 | filter: props.filter 108 | }) 109 | } 110 | > 111 | {props.children} 112 | 113 | ); 114 | } 115 | } 116 | 117 | const Footer = () => ( 118 |

    119 | Show: 120 | {' '} 121 | 122 | All 123 | 124 | {', '} 125 | 126 | Active 127 | 128 | {', '} 129 | 130 | Completed 131 | 132 |

    133 | ); 134 | 135 | const Todo = ({ 136 | onClick, 137 | completed, 138 | text 139 | }) => ( 140 |
  • 149 | {text} 150 |
  • 151 | ); 152 | 153 | const TodoList = ({ 154 | todos, 155 | onTodoClick 156 | }) => ( 157 |
      158 | {todos.map(todo => 159 | onTodoClick(todo.id)} 163 | /> 164 | )} 165 |
    166 | ); 167 | 168 | let nextTodoId = 0; 169 | const AddTodo = () => { 170 | let input; 171 | 172 | return ( 173 |
    174 | { 175 | input = node; 176 | }} /> 177 | 187 |
    188 | ); 189 | }; 190 | 191 | const getVisibleTodos = ( 192 | todos, 193 | filter 194 | ) => { 195 | switch (filter) { 196 | case 'SHOW_ALL': 197 | return todos; 198 | case 'SHOW_COMPLETED': 199 | return todos.filter( 200 | t => t.completed 201 | ); 202 | case 'SHOW_ACTIVE': 203 | return todos.filter( 204 | t => !t.completed 205 | ); 206 | } 207 | } 208 | 209 | class VisibleTodoList extends Component { 210 | componentDidMount() { 211 | this.unsubscribe = store.subscribe(() => 212 | this.forceUpdate() 213 | ); 214 | } 215 | 216 | componentWillUnmount() { 217 | this.unsubscribe(); 218 | } 219 | 220 | render() { 221 | const props = this.props; 222 | const state = store.getState(); 223 | 224 | return ( 225 | 233 | store.dispatch({ 234 | type: 'TOGGLE_TODO', 235 | id 236 | }) 237 | } 238 | /> 239 | ); 240 | } 241 | } 242 | 243 | const TodoApp = () => ( 244 |
    245 | 246 | 247 |
    248 |
    249 | ); 250 | 251 | ReactDOM.render( 252 | , 253 | document.getElementById('root') 254 | ); -------------------------------------------------------------------------------- /24-react-redux-passing-the-store-down-explicitly-via-props/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/24-react-redux-passing-the-store-down-explicitly-via-props/app.css -------------------------------------------------------------------------------- /24-react-redux-passing-the-store-down-explicitly-via-props/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /24-react-redux-passing-the-store-down-explicitly-via-props/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { Component } = React; 58 | 59 | const Link = ({ 60 | active, 61 | children, 62 | onClick 63 | }) => { 64 | if (active) { 65 | return {children}; 66 | } 67 | 68 | return ( 69 | { 71 | e.preventDefault(); 72 | onClick(); 73 | }} 74 | > 75 | {children} 76 | 77 | ); 78 | }; 79 | 80 | class FilterLink extends Component { 81 | componentDidMount() { 82 | const { store } = this.props; 83 | this.unsubscribe = store.subscribe(() => 84 | this.forceUpdate() 85 | ); 86 | } 87 | 88 | componentWillUnmount() { 89 | this.unsubscribe(); 90 | } 91 | 92 | render() { 93 | const props = this.props; 94 | const { store } = props; 95 | const state = store.getState(); 96 | 97 | return ( 98 | 104 | store.dispatch({ 105 | type: 'SET_VISIBILITY_FILTER', 106 | filter: props.filter 107 | }) 108 | } 109 | > 110 | {props.children} 111 | 112 | ); 113 | } 114 | } 115 | 116 | const Footer = ({ store }) => ( 117 |

    118 | Show: 119 | {' '} 120 | 124 | All 125 | 126 | {', '} 127 | 131 | Active 132 | 133 | {', '} 134 | 138 | Completed 139 | 140 |

    141 | ); 142 | 143 | const Todo = ({ 144 | onClick, 145 | completed, 146 | text 147 | }) => ( 148 |
  • 157 | {text} 158 |
  • 159 | ); 160 | 161 | const TodoList = ({ 162 | todos, 163 | onTodoClick 164 | }) => ( 165 |
      166 | {todos.map(todo => 167 | onTodoClick(todo.id)} 171 | /> 172 | )} 173 |
    174 | ); 175 | 176 | let nextTodoId = 0; 177 | const AddTodo = ({ store }) => { 178 | let input; 179 | 180 | return ( 181 |
    182 | { 183 | input = node; 184 | }} /> 185 | 195 |
    196 | ); 197 | }; 198 | 199 | const getVisibleTodos = ( 200 | todos, 201 | filter 202 | ) => { 203 | switch (filter) { 204 | case 'SHOW_ALL': 205 | return todos; 206 | case 'SHOW_COMPLETED': 207 | return todos.filter( 208 | t => t.completed 209 | ); 210 | case 'SHOW_ACTIVE': 211 | return todos.filter( 212 | t => !t.completed 213 | ); 214 | } 215 | } 216 | 217 | class VisibleTodoList extends Component { 218 | componentDidMount() { 219 | const { store } = this.props; 220 | this.unsubscribe = store.subscribe(() => 221 | this.forceUpdate() 222 | ); 223 | } 224 | 225 | componentWillUnmount() { 226 | this.unsubscribe(); 227 | } 228 | 229 | render() { 230 | const props = this.props; 231 | const { store } = props; 232 | const state = store.getState(); 233 | 234 | return ( 235 | 243 | store.dispatch({ 244 | type: 'TOGGLE_TODO', 245 | id 246 | }) 247 | } 248 | /> 249 | ); 250 | } 251 | } 252 | 253 | const TodoApp = ({ store }) => ( 254 |
    255 | 256 | 257 |
    258 |
    259 | ); 260 | 261 | 262 | const { createStore } = Redux; 263 | 264 | ReactDOM.render( 265 | , 266 | document.getElementById('root') 267 | ); -------------------------------------------------------------------------------- /25-react-redux-passing-the-store-down-implicitly-via-context/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/25-react-redux-passing-the-store-down-implicitly-via-context/app.css -------------------------------------------------------------------------------- /25-react-redux-passing-the-store-down-implicitly-via-context/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /25-react-redux-passing-the-store-down-implicitly-via-context/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { Component } = React; 58 | 59 | const Link = ({ 60 | active, 61 | children, 62 | onClick 63 | }) => { 64 | if (active) { 65 | return {children}; 66 | } 67 | 68 | return ( 69 | { 71 | e.preventDefault(); 72 | onClick(); 73 | }} 74 | > 75 | {children} 76 | 77 | ); 78 | }; 79 | 80 | class FilterLink extends Component { 81 | componentDidMount() { 82 | const { store } = this.context; 83 | this.unsubscribe = store.subscribe(() => 84 | this.forceUpdate() 85 | ); 86 | } 87 | 88 | componentWillUnmount() { 89 | this.unsubscribe(); 90 | } 91 | 92 | render() { 93 | const props = this.props; 94 | const { store } = this.context; 95 | const state = store.getState(); 96 | 97 | return ( 98 | 104 | store.dispatch({ 105 | type: 'SET_VISIBILITY_FILTER', 106 | filter: props.filter 107 | }) 108 | } 109 | > 110 | {props.children} 111 | 112 | ); 113 | } 114 | } 115 | FilterLink.contextTypes = { 116 | store: React.PropTypes 117 | }; 118 | 119 | const Footer = () => ( 120 |

    121 | Show: 122 | {' '} 123 | 126 | All 127 | 128 | {', '} 129 | 132 | Active 133 | 134 | {', '} 135 | 138 | Completed 139 | 140 |

    141 | ); 142 | 143 | const Todo = ({ 144 | onClick, 145 | completed, 146 | text 147 | }) => ( 148 |
  • 157 | {text} 158 |
  • 159 | ); 160 | 161 | const TodoList = ({ 162 | todos, 163 | onTodoClick 164 | }) => ( 165 |
      166 | {todos.map(todo => 167 | onTodoClick(todo.id)} 171 | /> 172 | )} 173 |
    174 | ); 175 | 176 | let nextTodoId = 0; 177 | const AddTodo = (props, { store }) => { 178 | let input; 179 | 180 | return ( 181 |
    182 | { 183 | input = node; 184 | }} /> 185 | 195 |
    196 | ); 197 | }; 198 | AddTodo.contextTypes = { 199 | store: React.PropTypes 200 | }; 201 | 202 | const getVisibleTodos = ( 203 | todos, 204 | filter 205 | ) => { 206 | switch (filter) { 207 | case 'SHOW_ALL': 208 | return todos; 209 | case 'SHOW_COMPLETED': 210 | return todos.filter( 211 | t => t.completed 212 | ); 213 | case 'SHOW_ACTIVE': 214 | return todos.filter( 215 | t => !t.completed 216 | ); 217 | } 218 | } 219 | 220 | class VisibleTodoList extends Component { 221 | componentDidMount() { 222 | const { store } = this.context; 223 | this.unsubscribe = store.subscribe(() => 224 | this.forceUpdate() 225 | ); 226 | } 227 | 228 | componentWillUnmount() { 229 | this.unsubscribe(); 230 | } 231 | 232 | render() { 233 | const props = this.props; 234 | const { store } = this.context; 235 | const state = store.getState(); 236 | 237 | return ( 238 | 246 | store.dispatch({ 247 | type: 'TOGGLE_TODO', 248 | id 249 | }) 250 | } 251 | /> 252 | ); 253 | } 254 | } 255 | VisibleTodoList.contextTypes = { 256 | store: React.PropTypes 257 | }; 258 | 259 | const TodoApp = () => ( 260 |
    261 | 262 | 263 |
    264 |
    265 | ); 266 | 267 | class Provider extends Component { 268 | getChildContext() { 269 | return { 270 | store: this.props.store 271 | }; 272 | } 273 | 274 | render() { 275 | return this.props.children; 276 | } 277 | } 278 | Provider.childContextTypes = { 279 | store: React.PropTypes 280 | }; 281 | 282 | const { createStore } = Redux; 283 | 284 | ReactDOM.render( 285 | 286 | 287 | , 288 | document.getElementById('root') 289 | ); -------------------------------------------------------------------------------- /26-react-redux-passing-the-store-down-with-provider-from-react-redux/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/26-react-redux-passing-the-store-down-with-provider-from-react-redux/app.css -------------------------------------------------------------------------------- /26-react-redux-passing-the-store-down-with-provider-from-react-redux/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /26-react-redux-passing-the-store-down-with-provider-from-react-redux/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { Component } = React; 58 | 59 | const Link = ({ 60 | active, 61 | children, 62 | onClick 63 | }) => { 64 | if (active) { 65 | return {children}; 66 | } 67 | 68 | return ( 69 | { 71 | e.preventDefault(); 72 | onClick(); 73 | }} 74 | > 75 | {children} 76 | 77 | ); 78 | }; 79 | 80 | class FilterLink extends Component { 81 | componentDidMount() { 82 | const { store } = this.context; 83 | this.unsubscribe = store.subscribe(() => 84 | this.forceUpdate() 85 | ); 86 | } 87 | 88 | componentWillUnmount() { 89 | this.unsubscribe(); 90 | } 91 | 92 | render() { 93 | const props = this.props; 94 | const { store } = this.context; 95 | const state = store.getState(); 96 | 97 | return ( 98 | 104 | store.dispatch({ 105 | type: 'SET_VISIBILITY_FILTER', 106 | filter: props.filter 107 | }) 108 | } 109 | > 110 | {props.children} 111 | 112 | ); 113 | } 114 | } 115 | FilterLink.contextTypes = { 116 | store: React.PropTypes 117 | }; 118 | 119 | const Footer = () => ( 120 |

    121 | Show: 122 | {' '} 123 | 124 | All 125 | 126 | {', '} 127 | 128 | Active 129 | 130 | {', '} 131 | 132 | Completed 133 | 134 |

    135 | ); 136 | 137 | const Todo = ({ 138 | onClick, 139 | completed, 140 | text 141 | }) => ( 142 |
  • 151 | {text} 152 |
  • 153 | ); 154 | 155 | const TodoList = ({ 156 | todos, 157 | onTodoClick 158 | }) => ( 159 |
      160 | {todos.map(todo => 161 | onTodoClick(todo.id)} 165 | /> 166 | )} 167 |
    168 | ); 169 | 170 | let nextTodoId = 0; 171 | const AddTodo = (props, { store }) => { 172 | let input; 173 | 174 | return ( 175 |
    176 | { 177 | input = node; 178 | }} /> 179 | 189 |
    190 | ); 191 | }; 192 | AddTodo.contextTypes = { 193 | store: React.PropTypes 194 | }; 195 | 196 | const getVisibleTodos = ( 197 | todos, 198 | filter 199 | ) => { 200 | switch (filter) { 201 | case 'SHOW_ALL': 202 | return todos; 203 | case 'SHOW_COMPLETED': 204 | return todos.filter( 205 | t => t.completed 206 | ); 207 | case 'SHOW_ACTIVE': 208 | return todos.filter( 209 | t => !t.completed 210 | ); 211 | } 212 | } 213 | 214 | class VisibleTodoList extends Component { 215 | componentDidMount() { 216 | const { store } = this.context; 217 | this.unsubscribe = store.subscribe(() => 218 | this.forceUpdate() 219 | ); 220 | } 221 | 222 | componentWillUnmount() { 223 | this.unsubscribe(); 224 | } 225 | 226 | render() { 227 | const props = this.props; 228 | const { store } = this.context; 229 | const state = store.getState(); 230 | 231 | return ( 232 | 240 | store.dispatch({ 241 | type: 'TOGGLE_TODO', 242 | id 243 | }) 244 | } 245 | /> 246 | ); 247 | } 248 | } 249 | VisibleTodoList.contextTypes = { 250 | store: React.PropTypes 251 | }; 252 | 253 | const TodoApp = () => ( 254 |
    255 | 256 | 257 |
    258 |
    259 | ); 260 | 261 | const { Provider } = ReactRedux; 262 | const { createStore } = Redux; 263 | 264 | ReactDOM.render( 265 | 266 | 267 | , 268 | document.getElementById('root') 269 | ); -------------------------------------------------------------------------------- /27-react-redux-generating-containers-with-connect-from-react-redux-visibletodolist/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/27-react-redux-generating-containers-with-connect-from-react-redux-visibletodolist/app.css -------------------------------------------------------------------------------- /27-react-redux-generating-containers-with-connect-from-react-redux-visibletodolist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /27-react-redux-generating-containers-with-connect-from-react-redux-visibletodolist/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { Component } = React; 58 | 59 | const Link = ({ 60 | active, 61 | children, 62 | onClick 63 | }) => { 64 | if (active) { 65 | return {children}; 66 | } 67 | 68 | return ( 69 | { 71 | e.preventDefault(); 72 | onClick(); 73 | }} 74 | > 75 | {children} 76 | 77 | ); 78 | }; 79 | 80 | class FilterLink extends Component { 81 | componentDidMount() { 82 | const { store } = this.context; 83 | this.unsubscribe = store.subscribe(() => 84 | this.forceUpdate() 85 | ); 86 | } 87 | 88 | componentWillUnmount() { 89 | this.unsubscribe(); 90 | } 91 | 92 | render() { 93 | const props = this.props; 94 | const { store } = this.context; 95 | const state = store.getState(); 96 | 97 | return ( 98 | 104 | store.dispatch({ 105 | type: 'SET_VISIBILITY_FILTER', 106 | filter: props.filter 107 | }) 108 | } 109 | > 110 | {props.children} 111 | 112 | ); 113 | } 114 | } 115 | FilterLink.contextTypes = { 116 | store: React.PropTypes 117 | }; 118 | 119 | const Footer = () => ( 120 |

    121 | Show: 122 | {' '} 123 | 124 | All 125 | 126 | {', '} 127 | 128 | Active 129 | 130 | {', '} 131 | 132 | Completed 133 | 134 |

    135 | ); 136 | 137 | const Todo = ({ 138 | onClick, 139 | completed, 140 | text 141 | }) => ( 142 |
  • 151 | {text} 152 |
  • 153 | ); 154 | 155 | const TodoList = ({ 156 | todos, 157 | onTodoClick 158 | }) => ( 159 |
      160 | {todos.map(todo => 161 | onTodoClick(todo.id)} 165 | /> 166 | )} 167 |
    168 | ); 169 | 170 | let nextTodoId = 0; 171 | const AddTodo = (props, { store }) => { 172 | let input; 173 | 174 | return ( 175 |
    176 | { 177 | input = node; 178 | }} /> 179 | 189 |
    190 | ); 191 | }; 192 | AddTodo.contextTypes = { 193 | store: React.PropTypes 194 | }; 195 | 196 | const getVisibleTodos = ( 197 | todos, 198 | filter 199 | ) => { 200 | switch (filter) { 201 | case 'SHOW_ALL': 202 | return todos; 203 | case 'SHOW_COMPLETED': 204 | return todos.filter( 205 | t => t.completed 206 | ); 207 | case 'SHOW_ACTIVE': 208 | return todos.filter( 209 | t => !t.completed 210 | ); 211 | } 212 | } 213 | 214 | const mapStateToProps = (state) => { 215 | return { 216 | todos: getVisibleTodos( 217 | state.todos, 218 | state.visibilityFilter 219 | ) 220 | }; 221 | }; 222 | 223 | const mapDispatchToProps = (dispatch) => { 224 | return { 225 | onTodoClick: (id) => { 226 | dispatch({ 227 | type: 'TOGGLE_TODO', 228 | id 229 | }); 230 | } 231 | }; 232 | }; 233 | 234 | const { connect } = ReactRedux; 235 | const VisibleTodoList = connect( 236 | mapStateToProps, 237 | mapDispatchToProps 238 | )(TodoList); 239 | 240 | const TodoApp = () => ( 241 |
    242 | 243 | 244 |
    245 |
    246 | ); 247 | 248 | const { Provider } = ReactRedux; 249 | const { createStore } = Redux; 250 | 251 | ReactDOM.render( 252 | 253 | 254 | , 255 | document.getElementById('root') 256 | ); -------------------------------------------------------------------------------- /28-react-redux-generating-containers-with-connect-from-react-redux-addtodo/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/28-react-redux-generating-containers-with-connect-from-react-redux-addtodo/app.css -------------------------------------------------------------------------------- /28-react-redux-generating-containers-with-connect-from-react-redux-addtodo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /28-react-redux-generating-containers-with-connect-from-react-redux-addtodo/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { Component } = React; 58 | const { connect } = ReactRedux; 59 | 60 | const Link = ({ 61 | active, 62 | children, 63 | onClick 64 | }) => { 65 | if (active) { 66 | return {children}; 67 | } 68 | 69 | return ( 70 | { 72 | e.preventDefault(); 73 | onClick(); 74 | }} 75 | > 76 | {children} 77 | 78 | ); 79 | }; 80 | 81 | class FilterLink extends Component { 82 | componentDidMount() { 83 | const { store } = this.context; 84 | this.unsubscribe = store.subscribe(() => 85 | this.forceUpdate() 86 | ); 87 | } 88 | 89 | componentWillUnmount() { 90 | this.unsubscribe(); 91 | } 92 | 93 | render() { 94 | const props = this.props; 95 | const { store } = this.context; 96 | const state = store.getState(); 97 | 98 | return ( 99 | 105 | store.dispatch({ 106 | type: 'SET_VISIBILITY_FILTER', 107 | filter: props.filter 108 | }) 109 | } 110 | > 111 | {props.children} 112 | 113 | ); 114 | } 115 | } 116 | FilterLink.contextTypes = { 117 | store: React.PropTypes 118 | }; 119 | 120 | const Footer = () => ( 121 |

    122 | Show: 123 | {' '} 124 | 125 | All 126 | 127 | {', '} 128 | 129 | Active 130 | 131 | {', '} 132 | 133 | Completed 134 | 135 |

    136 | ); 137 | 138 | const Todo = ({ 139 | onClick, 140 | completed, 141 | text 142 | }) => ( 143 |
  • 152 | {text} 153 |
  • 154 | ); 155 | 156 | const TodoList = ({ 157 | todos, 158 | onTodoClick 159 | }) => ( 160 |
      161 | {todos.map(todo => 162 | onTodoClick(todo.id)} 166 | /> 167 | )} 168 |
    169 | ); 170 | 171 | let nextTodoId = 0; 172 | let AddTodo = ({ dispatch }) => { 173 | let input; 174 | 175 | return ( 176 |
    177 | { 178 | input = node; 179 | }} /> 180 | 190 |
    191 | ); 192 | }; 193 | AddTodo = connect()(AddTodo); 194 | 195 | const getVisibleTodos = ( 196 | todos, 197 | filter 198 | ) => { 199 | switch (filter) { 200 | case 'SHOW_ALL': 201 | return todos; 202 | case 'SHOW_COMPLETED': 203 | return todos.filter( 204 | t => t.completed 205 | ); 206 | case 'SHOW_ACTIVE': 207 | return todos.filter( 208 | t => !t.completed 209 | ); 210 | } 211 | } 212 | 213 | const mapStateToTodoListProps = ( 214 | state 215 | ) => { 216 | return { 217 | todos: getVisibleTodos( 218 | state.todos, 219 | state.visibilityFilter 220 | ) 221 | }; 222 | }; 223 | const mapDispatchToTodoListProps = ( 224 | dispatch 225 | ) => { 226 | return { 227 | onTodoClick: (id) => { 228 | dispatch({ 229 | type: 'TOGGLE_TODO', 230 | id 231 | }); 232 | } 233 | }; 234 | }; 235 | const VisibleTodoList = connect( 236 | mapStateToTodoListProps, 237 | mapDispatchToTodoListProps 238 | )(TodoList); 239 | 240 | const TodoApp = () => ( 241 |
    242 | 243 | 244 |
    245 |
    246 | ); 247 | 248 | const { Provider } = ReactRedux; 249 | const { createStore } = Redux; 250 | 251 | ReactDOM.render( 252 | 253 | 254 | , 255 | document.getElementById('root') 256 | ); -------------------------------------------------------------------------------- /29-react-redux-generating-containers-with-connect-from-react-redux-footerlink/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/29-react-redux-generating-containers-with-connect-from-react-redux-footerlink/app.css -------------------------------------------------------------------------------- /29-react-redux-generating-containers-with-connect-from-react-redux-footerlink/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /29-react-redux-generating-containers-with-connect-from-react-redux-footerlink/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | const { Component } = React; 58 | const { connect } = ReactRedux; 59 | 60 | const Link = ({ 61 | active, 62 | children, 63 | onClick 64 | }) => { 65 | if (active) { 66 | return {children}; 67 | } 68 | 69 | return ( 70 | { 72 | e.preventDefault(); 73 | onClick(); 74 | }} 75 | > 76 | {children} 77 | 78 | ); 79 | }; 80 | 81 | const mapStateToLinkProps = ( 82 | state, 83 | ownProps 84 | ) => { 85 | return { 86 | active: 87 | ownProps.filter === 88 | state.visibilityFilter 89 | }; 90 | }; 91 | const mapDispatchToLinkProps = ( 92 | dispatch, 93 | ownProps 94 | ) => { 95 | return { 96 | onClick: () => { 97 | dispatch({ 98 | type: 'SET_VISIBILITY_FILTER', 99 | filter: ownProps.filter 100 | }); 101 | } 102 | }; 103 | } 104 | const FilterLink = connect( 105 | mapStateToLinkProps, 106 | mapDispatchToLinkProps 107 | )(Link); 108 | 109 | const Footer = () => ( 110 |

    111 | Show: 112 | {' '} 113 | 114 | All 115 | 116 | {', '} 117 | 118 | Active 119 | 120 | {', '} 121 | 122 | Completed 123 | 124 |

    125 | ); 126 | 127 | const Todo = ({ 128 | onClick, 129 | completed, 130 | text 131 | }) => ( 132 |
  • 141 | {text} 142 |
  • 143 | ); 144 | 145 | const TodoList = ({ 146 | todos, 147 | onTodoClick 148 | }) => ( 149 |
      150 | {todos.map(todo => 151 | onTodoClick(todo.id)} 155 | /> 156 | )} 157 |
    158 | ); 159 | 160 | let nextTodoId = 0; 161 | let AddTodo = ({ dispatch }) => { 162 | let input; 163 | 164 | return ( 165 |
    166 | { 167 | input = node; 168 | }} /> 169 | 179 |
    180 | ); 181 | }; 182 | AddTodo = connect()(AddTodo); 183 | 184 | const getVisibleTodos = ( 185 | todos, 186 | filter 187 | ) => { 188 | switch (filter) { 189 | case 'SHOW_ALL': 190 | return todos; 191 | case 'SHOW_COMPLETED': 192 | return todos.filter( 193 | t => t.completed 194 | ); 195 | case 'SHOW_ACTIVE': 196 | return todos.filter( 197 | t => !t.completed 198 | ); 199 | } 200 | } 201 | 202 | const mapStateToTodoListProps = ( 203 | state 204 | ) => { 205 | return { 206 | todos: getVisibleTodos( 207 | state.todos, 208 | state.visibilityFilter 209 | ) 210 | }; 211 | }; 212 | const mapDispatchToTodoListProps = ( 213 | dispatch 214 | ) => { 215 | return { 216 | onTodoClick: (id) => { 217 | dispatch({ 218 | type: 'TOGGLE_TODO', 219 | id 220 | }); 221 | } 222 | }; 223 | }; 224 | const VisibleTodoList = connect( 225 | mapStateToTodoListProps, 226 | mapDispatchToTodoListProps 227 | )(TodoList); 228 | 229 | const TodoApp = () => ( 230 |
    231 | 232 | 233 |
    234 |
    235 | ); 236 | 237 | const { Provider } = ReactRedux; 238 | const { createStore } = Redux; 239 | 240 | ReactDOM.render( 241 | 242 | 243 | , 244 | document.getElementById('root') 245 | ); -------------------------------------------------------------------------------- /30-react-redux-extracting-action-creators/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eggheadio-projects/getting-started-with-redux/4e0c7b99f9b9e1fe8b2bbf5088f712bb4304d18d/30-react-redux-extracting-action-creators/app.css -------------------------------------------------------------------------------- /30-react-redux-extracting-action-creators/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plunker 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 | 16 | 17 | -------------------------------------------------------------------------------- /30-react-redux-extracting-action-creators/script.jsx: -------------------------------------------------------------------------------- 1 | const todo = (state, action) => { 2 | switch (action.type) { 3 | case 'ADD_TODO': 4 | return { 5 | id: action.id, 6 | text: action.text, 7 | completed: false 8 | }; 9 | case 'TOGGLE_TODO': 10 | if (state.id !== action.id) { 11 | return state; 12 | } 13 | 14 | return { 15 | ...state, 16 | completed: !state.completed 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const todos = (state = [], action) => { 24 | switch (action.type) { 25 | case 'ADD_TODO': 26 | return [ 27 | ...state, 28 | todo(undefined, action) 29 | ]; 30 | case 'TOGGLE_TODO': 31 | return state.map(t => 32 | todo(t, action) 33 | ); 34 | default: 35 | return state; 36 | } 37 | }; 38 | 39 | const visibilityFilter = ( 40 | state = 'SHOW_ALL', 41 | action 42 | ) => { 43 | switch (action.type) { 44 | case 'SET_VISIBILITY_FILTER': 45 | return action.filter; 46 | default: 47 | return state; 48 | } 49 | }; 50 | 51 | const { combineReducers } = Redux; 52 | const todoApp = combineReducers({ 53 | todos, 54 | visibilityFilter 55 | }); 56 | 57 | let nextTodoId = 0; 58 | const addTodo = (text) => { 59 | return { 60 | type: 'ADD_TODO', 61 | id: nextTodoId++, 62 | text 63 | }; 64 | }; 65 | 66 | const toggleTodo = (id) => { 67 | return { 68 | type: 'TOGGLE_TODO', 69 | id 70 | }; 71 | }; 72 | 73 | const setVisibilityFilter = (filter) => { 74 | return { 75 | type: 'SET_VISIBILITY_FILTER', 76 | filter 77 | }; 78 | }; 79 | 80 | const { Component } = React; 81 | const { Provider, connect } = ReactRedux; 82 | 83 | const Link = ({ 84 | active, 85 | children, 86 | onClick 87 | }) => { 88 | if (active) { 89 | return {children}; 90 | } 91 | 92 | return ( 93 | { 95 | e.preventDefault(); 96 | onClick(); 97 | }} 98 | > 99 | {children} 100 | 101 | ); 102 | }; 103 | 104 | const mapStateToLinkProps = ( 105 | state, 106 | ownProps 107 | ) => { 108 | return { 109 | active: 110 | ownProps.filter === 111 | state.visibilityFilter 112 | }; 113 | }; 114 | const mapDispatchToLinkProps = ( 115 | dispatch, 116 | ownProps 117 | ) => { 118 | return { 119 | onClick: () => { 120 | dispatch( 121 | setVisibilityFilter(ownProps.filter) 122 | ); 123 | } 124 | }; 125 | } 126 | const FilterLink = connect( 127 | mapStateToLinkProps, 128 | mapDispatchToLinkProps 129 | )(Link); 130 | 131 | const Footer = () => ( 132 |

    133 | Show: 134 | {' '} 135 | 136 | All 137 | 138 | {', '} 139 | 140 | Active 141 | 142 | {', '} 143 | 144 | Completed 145 | 146 |

    147 | ); 148 | 149 | const Todo = ({ 150 | onClick, 151 | completed, 152 | text 153 | }) => ( 154 |
  • 163 | {text} 164 |
  • 165 | ); 166 | 167 | const TodoList = ({ 168 | todos, 169 | onTodoClick 170 | }) => ( 171 |
      172 | {todos.map(todo => 173 | onTodoClick(todo.id)} 177 | /> 178 | )} 179 |
    180 | ); 181 | 182 | let AddTodo = ({ dispatch }) => { 183 | let input; 184 | 185 | return ( 186 |
    187 | { 188 | input = node; 189 | }} /> 190 | 196 |
    197 | ); 198 | }; 199 | AddTodo = connect()(AddTodo); 200 | 201 | const getVisibleTodos = ( 202 | todos, 203 | filter 204 | ) => { 205 | switch (filter) { 206 | case 'SHOW_ALL': 207 | return todos; 208 | case 'SHOW_COMPLETED': 209 | return todos.filter( 210 | t => t.completed 211 | ); 212 | case 'SHOW_ACTIVE': 213 | return todos.filter( 214 | t => !t.completed 215 | ); 216 | } 217 | } 218 | 219 | const mapStateToTodoListProps = ( 220 | state 221 | ) => { 222 | return { 223 | todos: getVisibleTodos( 224 | state.todos, 225 | state.visibilityFilter 226 | ) 227 | }; 228 | }; 229 | const mapDispatchToTodoListProps = ( 230 | dispatch 231 | ) => { 232 | return { 233 | onTodoClick: (id) => { 234 | dispatch(toggleTodo(id)); 235 | } 236 | }; 237 | }; 238 | const VisibleTodoList = connect( 239 | mapStateToTodoListProps, 240 | mapDispatchToTodoListProps 241 | )(TodoList); 242 | 243 | const TodoApp = () => ( 244 |
    245 | 246 | 247 |
    248 |
    249 | ); 250 | 251 | const { createStore } = Redux; 252 | 253 | ReactDOM.render( 254 | 255 | 256 | , 257 | document.getElementById('root') 258 | ); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Redux 2 | _by Dan Abramov_ 3 | 4 | ![](https://d2eip9sf3oo6c2.cloudfront.net/series/square_covers/000/000/025/full/EGH_Redux-New.png?1496436379) 5 | 6 | Managing state in an application is critical, and is often done haphazardly. Redux provides a state container for JavaScript applications that will help your applications behave consistently. 7 | Redux is an evolution of the ideas presented by Facebook's Flux, avoiding the complexity found in [Flux](https://egghead.io/articles/gentle-introduction-to-the-react-flux-architecture) by looking to how applications are built with the Elm language. 8 | Redux is useful for React applications, but React is not a requirement! 9 | In this series, we will learn the basics of Redux, so that you can start using it to simplify your applications. 10 | It's highly recommended that you have a quick read of the [Redux documentation](http://redux.js.org/docs/introduction/Motivation.html) alongside or before you watch the series. 11 | There are some amazing [community notes on this course here on Github](https://github.com/tayiorbeii/egghead.io_redux_course_notes. 12 | Once you are finished with this course be sure to check out part 2: [building-react-applications-with-idiomatic-redux](https://egghead.io/courses/building-react-applications-with-idiomatic-redux) --------------------------------------------------------------------------------