Example: Todo List

index.js

actions/index.js

  1. export const addTodo = text => ({
  2. type: 'ADD_TODO',
  3. id: nextTodoId++,
  4. text
  5. })
  6. export const setVisibilityFilter = filter => ({
  7. type: 'SET_VISIBILITY_FILTER',
  8. filter
  9. })
  10. export const toggleTodo = id => ({
  11. type: 'TOGGLE_TODO',
  12. id
  13. })
  14. export const VisibilityFilters = {
  15. SHOW_ALL: 'SHOW_ALL',
  16. SHOW_COMPLETED: 'SHOW_COMPLETED',
  17. SHOW_ACTIVE: 'SHOW_ACTIVE'
  18. }

reducers/todos.js

  1. const todos = (state = [], action) => {
  2. switch (action.type) {
  3. case 'ADD_TODO':
  4. return [
  5. ...state,
  6. {
  7. id: action.id,
  8. text: action.text,
  9. completed: false
  10. }
  11. ]
  12. case 'TOGGLE_TODO':
  13. return state.map(todo =>
  14. todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
  15. )
  16. default:
  17. return state
  18. }
  19. }
  20. export default todos

reducers/visibilityFilter.js

  1. import { VisibilityFilters } from '../actions'
  2. const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
  3. switch (action.type) {
  4. return action.filter
  5. default:
  6. return state
  7. }
  8. }
  9. export default visibilityFilter

reducers/index.js

components/Todo.js

  1. import React from 'react'
  2. import PropTypes from 'prop-types'
  3. const Todo = ({ onClick, completed, text }) => (
  4. <li
  5. style={{
  6. textDecoration: completed ? 'line-through' : 'none'
  7. }}
  8. >
  9. {text}
  10. </li>
  11. )
  12. Todo.propTypes = {
  13. onClick: PropTypes.func.isRequired,
  14. completed: PropTypes.bool.isRequired,
  15. text: PropTypes.string.isRequired
  16. }
  17. export default Todo

components/TodoList.js

  1. import React from 'react'
  2. import PropTypes from 'prop-types'
  3. import Todo from './Todo'
  4. const TodoList = ({ todos, toggleTodo }) => (
  5. <ul>
  6. {todos.map(todo => (
  7. <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} />
  8. ))}
  9. </ul>
  10. )
  11. TodoList.propTypes = {
  12. todos: PropTypes.arrayOf(
  13. PropTypes.shape({
  14. id: PropTypes.number.isRequired,
  15. completed: PropTypes.bool.isRequired,
  16. text: PropTypes.string.isRequired
  17. }).isRequired
  18. ).isRequired,
  19. toggleTodo: PropTypes.func.isRequired
  20. }
  21. export default TodoList
  1. import React from 'react'
  2. import PropTypes from 'prop-types'
  3. const Link = ({ active, children, onClick }) => (
  4. <button
  5. onClick={onClick}
  6. disabled={active}
  7. style={{
  8. marginLeft: '4px'
  9. }}
  10. >
  11. {children}
  12. </button>
  13. )
  14. Link.propTypes = {
  15. active: PropTypes.bool.isRequired,
  16. children: PropTypes.node.isRequired,
  17. onClick: PropTypes.func.isRequired
  18. }
  19. export default Link

components/App.js

  1. import React from 'react'
  2. import Footer from './Footer'
  3. import AddTodo from '../containers/AddTodo'
  4. import VisibleTodoList from '../containers/VisibleTodoList'
  5. const App = () => (
  6. <div>
  7. <AddTodo />
  8. <VisibleTodoList />
  9. <Footer />
  10. </div>
  11. )
  12. export default App

containers/VisibleTodoList.js

  1. import { connect } from 'react-redux'
  2. import { toggleTodo } from '../actions'
  3. import TodoList from '../components/TodoList'
  4. import { VisibilityFilters } from '../actions'
  5. const getVisibleTodos = (todos, filter) => {
  6. switch (filter) {
  7. case VisibilityFilters.SHOW_ALL:
  8. return todos
  9. case VisibilityFilters.SHOW_COMPLETED:
  10. return todos.filter(t => t.completed)
  11. case VisibilityFilters.SHOW_ACTIVE:
  12. return todos.filter(t => !t.completed)
  13. default:
  14. throw new Error('Unknown filter: ' + filter)
  15. }
  16. }
  17. const mapStateToProps = state => ({
  18. todos: getVisibleTodos(state.todos, state.visibilityFilter)
  19. })
  20. const mapDispatchToProps = dispatch => ({
  21. toggleTodo: id => dispatch(toggleTodo(id))
  22. })
  23. export default connect(
  24. mapStateToProps,
  25. mapDispatchToProps
  26. )(TodoList)
  1. import { connect } from 'react-redux'
  2. import { setVisibilityFilter } from '../actions'
  3. import Link from '../components/Link'
  4. const mapStateToProps = (state, ownProps) => ({
  5. active: ownProps.filter === state.visibilityFilter
  6. })
  7. const mapDispatchToProps = (dispatch, ownProps) => ({
  8. onClick: () => dispatch(setVisibilityFilter(ownProps.filter))
  9. })
  10. export default connect(
  11. mapStateToProps,

containers/AddTodo.js