Last active
March 11, 2016 01:58
-
-
Save jsteenkamp/b7a75421c953f8b8c933 to your computer and use it in GitHub Desktop.
Commented EggHead Redux Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Reducers are prue functions that implement the update logic of the applications | |
// Reducers define how to calculate the next State from the current State and the dispatched Action | |
// Reducer function returns the current State if not Action is performed. | |
// Redux passes the new State to each component and React re-renders them. | |
// Reducers are typically called by Container components. Container components do not emit DOM (so they | |
// do not have any associated CSS) | |
const todo = (state, action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return { | |
id: action.id, | |
text: action.text, | |
completed: false | |
}; | |
case 'TOGGLE_TODO': | |
if (state.id !== action.id) { | |
return state; | |
} | |
return { | |
...state, | |
completed: !state.completed | |
}; | |
default: | |
return state; | |
} | |
}; | |
const todos = (state = [], action) => { | |
switch (action.type) { | |
case 'ADD_TODO': | |
return [ | |
...state, | |
todo(undefined, action) | |
]; | |
case 'TOGGLE_TODO': | |
return state.map(t => | |
todo(t, action) | |
); | |
default: | |
return state; | |
} | |
}; | |
const visibilityFilter = ( | |
state = 'SHOW_ALL', | |
action | |
) => { | |
switch (action.type) { | |
case 'SET_VISIBILITY_FILTER': | |
return action.filter; | |
default: | |
return state; | |
} | |
}; | |
// Reducer compositon - create smaller reducers focussed on a specifci concern | |
// Each reducer specifies how different parts of the state tree are updated in response to actions | |
// Use the convention of using the same name for the reducer and it's corresponding state key | |
// then you can use ES6 object literal shorthand notation | |
const { combineReducers } = Redux; | |
const todoApp = combineReducers({ | |
todos, | |
visibilityFilter | |
}); | |
// Actions describe what happens in the application. | |
// Action Creators formalize Actions into reusable JSON action objects that can be dispatched | |
// by other components if required. The Action Creator must return an object with type key/value. | |
// An action is the minimal representation of the change to the application state (data). | |
// Any data that gets into a Redux application gets there by actions. | |
// The structure of the action object is up to you. The only requirement is that it has a type property, | |
// which is not undefined. We suggest using strings, because they are serializable. | |
// You can also use ES6 Symbols: | |
// const ADD_TODO = Symbol('ADD_TODO'); | |
// and then use type: ADD_TODO | |
let nextTodoId = 0; | |
const addTodo = (text) => { | |
return { | |
type: 'ADD_TODO', | |
id: nextTodoId++, | |
text | |
}; | |
}; | |
const toggleTodo = (id) => { | |
return { | |
type: 'TOGGLE_TODO', | |
id | |
}; | |
}; | |
const setVisibilityFilter = (filter) => { | |
return { | |
type: 'SET_VISIBILITY_FILTER', | |
filter | |
}; | |
}; | |
// Components | |
// Provider and connect are used to bind Redux and React, Provider sets up the store | |
// and connect is used to subscribe to the store state and dispatch update actions | |
const { Provider, connect } = ReactRedux; | |
const Link = ({ | |
active, | |
children, | |
onClick | |
}) => { | |
if (active) { | |
return <span>{children}</span>; | |
} | |
return ( | |
<a href='#' | |
onClick={e => { | |
e.preventDefault(); | |
onClick(); | |
}} | |
> | |
{children} | |
</a> | |
); | |
}; | |
const mapStateToLinkProps = ( | |
state, | |
ownProps | |
) => { | |
return { | |
active: | |
ownProps.filter === | |
state.visibilityFilter | |
}; | |
}; | |
const mapDispatchToLinkProps = ( | |
dispatch, | |
ownProps | |
) => { | |
return { | |
onClick: () => { | |
dispatch( | |
setVisibilityFilter(ownProps.filter) | |
); | |
} | |
}; | |
} | |
// connect() connects a React component to a Redux store. | |
// It does not modify the component class passed to it. | |
// Instead, it returns a new, connected component class, for you to use. | |
// connect() generates a container component that wraps the component we pass in | |
// and injects subscribe and dispatch objects. If we do not pass properties (null) | |
// the connect(null, null) default is not to subscribe to store and to pass dispatch as a prop | |
// By using connect() we do not have to manually subscribe/unsubscribe/dispatch and use the | |
// React componentDidMount() and componentWillUnmount() methods | |
const FilterLink = connect( | |
mapStateToLinkProps, | |
mapDispatchToLinkProps | |
)(Link); | |
const Footer = () => ( | |
<p> | |
Show: | |
{' '} | |
<FilterLink filter='SHOW_ALL'> | |
All | |
</FilterLink> | |
{', '} | |
<FilterLink filter='SHOW_ACTIVE'> | |
Active | |
</FilterLink> | |
{', '} | |
<FilterLink filter='SHOW_COMPLETED'> | |
Completed | |
</FilterLink> | |
</p> | |
); | |
const Todo = ({ | |
onClick, | |
completed, | |
text | |
}) => ( | |
<li | |
onClick={onClick} | |
style={{ | |
textDecoration: | |
completed ? | |
'line-through' : | |
'none' | |
}} | |
> | |
{text} | |
</li> | |
); | |
// Note that here we use let since we reuse this component after wrapping | |
// it in a container with connect() and reuse the component. We can do this as we | |
// do not need to map state and just need access to dispatch. | |
// Using connect() = connect(null, null) = connect(null, dispatch) | |
// so default is not to subscribe to store and pass dispatch as property | |
let AddTodo = ({ dispatch }) => { | |
let input; | |
return ( | |
<div> | |
<input ref={node => { | |
input = node; | |
}} /> | |
<button onClick={() => { | |
dispatch(addTodo(input.value)); | |
input.value = ''; | |
}}> | |
Add Todo | |
</button> | |
</div> | |
); | |
}; | |
AddTodo = connect()(AddTodo); | |
const getVisibleTodos = ( | |
todos, | |
filter | |
) => { | |
switch (filter) { | |
case 'SHOW_ALL': | |
return todos; | |
case 'SHOW_COMPLETED': | |
return todos.filter( | |
t => t.completed | |
); | |
case 'SHOW_ACTIVE': | |
return todos.filter( | |
t => !t.completed | |
); | |
} | |
} | |
const TodoList = ({ | |
todos, | |
onTodoClick | |
}) => ( | |
<ul> | |
{todos.map(todo => | |
<Todo | |
key={todo.id} | |
{...todo} | |
onClick={() => onTodoClick(todo.id)} | |
/> | |
)} | |
</ul> | |
); | |
const mapStateToTodoListProps = ( | |
state | |
) => { | |
return { | |
todos: getVisibleTodos( | |
state.todos, | |
state.visibilityFilter | |
) | |
}; | |
}; | |
const mapDispatchToTodoListProps = ( | |
dispatch | |
) => { | |
return { | |
onTodoClick: (id) => { | |
dispatch(toggleTodo(id)); | |
} | |
}; | |
}; | |
const VisibleTodoList = connect( | |
mapStateToTodoListProps, | |
mapDispatchToTodoListProps | |
)(TodoList); | |
// main app component that is inserted into DOM | |
const TodoApp = () => ( | |
<div> | |
<AddTodo /> | |
<VisibleTodoList /> | |
<Footer /> | |
</div> | |
); | |
const { createStore } = Redux; | |
ReactDOM.render( | |
<Provider store={createStore(todoApp)}> | |
<TodoApp /> | |
</Provider>, | |
document.getElementById('root') | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment