Redux

Redux

  • Getting Started
  • API
  • FAQ
  • Github
  • Need help?

›Basic Tutorial

Introduction

  • Getting Started with Redux
  • Installation
  • Motivation
  • Core Concepts
  • Three Principles
  • Prior Art
  • Learning Resources
  • Ecosystem
  • Examples

Basic Tutorial

  • Basic Tutorial: Intro
  • Actions
  • Reducers
  • Store
  • Data flow
  • Usage with React
  • Example: Todo List

Advanced Tutorial

  • Advanced Tutorial: Intro
  • Async Actions
  • Async Flow
  • Middleware
  • Usage with React Router
  • Example: Reddit API
  • Next Steps

Recipes

  • Recipes: Index
  • Configuring Your Store
  • Usage With TypeScript
  • Migrating to Redux
  • Using Object Spread Operator
  • Reducing Boilerplate
  • Server Rendering
  • Writing Tests
  • Computing Derived Data
  • Implementing Undo History
  • Isolating Redux Sub-Apps
  • Using Immutable.JS with Redux
  • Code Splitting
  • Structuring Reducers

    • Structuring Reducers: Intro
    • Prerequisite Concepts
    • Basic Reducer Structure
    • Splitting Reducer Logic
    • Refactoring Reducers Example
    • Using combineReducers
    • Beyond combineReducers
    • Normalizing State Shape
    • Updating Normalized Data
    • Reusing Reducer Logic
    • Immutable Update Patterns
    • Initializing State

FAQ

  • FAQ Index
  • General
  • Reducers
  • Organizing State
  • Store Setup
  • Actions
  • Immutable Data
  • Code Structure
  • Performance
  • Design Decisions
  • React Redux
  • Miscellaneous

Other

  • Glossary
  • Troubleshooting
  • Feedback

API Reference

  • API Reference
  • createStore
  • Store
  • combineReducers
  • applyMiddleware
  • bindActionCreators
  • compose

Example: Todo List

This is the complete source code of the tiny todo app we built during the basics tutorial. This code is also in our repository of examples and can be run in your browser via CodeSandbox.

Entry Point

index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import rootReducer from './reducers'
import App from './components/App'

const store = createStore(rootReducer)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

Action Creators

actions/index.js

let nextTodoId = 0
export const addTodo = (text) => ({
  type: 'ADD_TODO',
  id: nextTodoId++,
  text,
})

export const setVisibilityFilter = (filter) => ({
  type: 'SET_VISIBILITY_FILTER',
  filter,
})

export const toggleTodo = (id) => ({
  type: 'TOGGLE_TODO',
  id,
})

export const VisibilityFilters = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE',
}

Reducers

reducers/todos.js

const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false,
        },
      ]
    case 'TOGGLE_TODO':
      return state.map((todo) =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      )
    default:
      return state
  }
}

export default todos

reducers/visibilityFilter.js

import { VisibilityFilters } from '../actions'

const visibilityFilter = (state = VisibilityFilters.SHOW_ALL, action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

export default visibilityFilter

reducers/index.js

import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'

export default combineReducers({
  todos,
  visibilityFilter,
})

Presentational Components

components/Todo.js

import React from 'react'
import PropTypes from 'prop-types'

const Todo = ({ onClick, completed, text }) => (
  <li
    onClick={onClick}
    style={{
      textDecoration: completed ? 'line-through' : 'none',
    }}
  >
    {text}
  </li>
)

Todo.propTypes = {
  onClick: PropTypes.func.isRequired,
  completed: PropTypes.bool.isRequired,
  text: PropTypes.string.isRequired,
}

export default Todo

components/TodoList.js

import React from 'react'
import PropTypes from 'prop-types'
import Todo from './Todo'

const TodoList = ({ todos, toggleTodo }) => (
  <ul>
    {todos.map((todo) => (
      <Todo key={todo.id} {...todo} onClick={() => toggleTodo(todo.id)} />
    ))}
  </ul>
)

TodoList.propTypes = {
  todos: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      completed: PropTypes.bool.isRequired,
      text: PropTypes.string.isRequired,
    }).isRequired
  ).isRequired,
  toggleTodo: PropTypes.func.isRequired,
}

export default TodoList

components/Link.js

import React from 'react'
import PropTypes from 'prop-types'

const Link = ({ active, children, onClick }) => (
  <button
    onClick={onClick}
    disabled={active}
    style={{
      marginLeft: '4px',
    }}
  >
    {children}
  </button>
)

Link.propTypes = {
  active: PropTypes.bool.isRequired,
  children: PropTypes.node.isRequired,
  onClick: PropTypes.func.isRequired,
}

export default Link

components/Footer.js

import React from 'react'
import FilterLink from '../containers/FilterLink'
import { VisibilityFilters } from '../actions'

const Footer = () => (
  <div>
    <span>Show: </span>
    <FilterLink filter={VisibilityFilters.SHOW_ALL}>All</FilterLink>
    <FilterLink filter={VisibilityFilters.SHOW_ACTIVE}>Active</FilterLink>
    <FilterLink filter={VisibilityFilters.SHOW_COMPLETED}>Completed</FilterLink>
  </div>
)

export default Footer

components/App.js

import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'

const App = () => (
  <div>
    <AddTodo />
    <VisibleTodoList />
    <Footer />
  </div>
)

export default App

Container Components

containers/VisibleTodoList.js

import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { VisibilityFilters } from '../actions'

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case VisibilityFilters.SHOW_ALL:
      return todos
    case VisibilityFilters.SHOW_COMPLETED:
      return todos.filter((t) => t.completed)
    case VisibilityFilters.SHOW_ACTIVE:
      return todos.filter((t) => !t.completed)
    default:
      throw new Error('Unknown filter: ' + filter)
  }
}

const mapStateToProps = (state) => ({
  todos: getVisibleTodos(state.todos, state.visibilityFilter),
})

const mapDispatchToProps = (dispatch) => ({
  toggleTodo: (id) => dispatch(toggleTodo(id)),
})

export default connect(mapStateToProps, mapDispatchToProps)(TodoList)

containers/FilterLink.js

import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => ({
  active: ownProps.filter === state.visibilityFilter,
})

const mapDispatchToProps = (dispatch, ownProps) => ({
  onClick: () => dispatch(setVisibilityFilter(ownProps.filter)),
})

export default connect(mapStateToProps, mapDispatchToProps)(Link)

Other Components

containers/AddTodo.js

import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'

const AddTodo = ({ dispatch }) => {
  let input

  return (
    <div>
      <form
        onSubmit={(e) => {
          e.preventDefault()
          if (!input.value.trim()) {
            return
          }
          dispatch(addTodo(input.value))
          input.value = ''
        }}
      >
        <input ref={(node) => (input = node)} />
        <button type="submit">Add Todo</button>
      </form>
    </div>
  )
}

export default connect()(AddTodo)
Last updated on 4/2/2021
← Usage with ReactAdvanced Tutorial: Intro →
  • Entry Point
  • Action Creators
  • Reducers
  • Presentational Components
  • Container Components
    • Other Components
Redux
Docs
Getting StartedCore ConceptsBasicsAdvanced
Community
Stack OverflowDiscord
More
GitHubStar
Copyright (c) 2015-present Dan Abramov and the Redux documentation authors.
Some icons copyright Font Awesome and Noun Project (Hassan ali, ProSymbols)