Redux example analysis: todos

Code source: https://github.com/reactjs/redux/tree/master/examples/todos
source folder structure (excluding tests, i.e. spec.js end file):

- actions
    - index.js
- components
    - App.js
    - Footer.js
    - Link.js
    - Todo.js
    - TodoList.js
- containers
    - AddTodo.js
    - FilterLink.js
    - VisibleTodoList.js
- reducers
    - index.js
    - todos.js
    - visibilityFilter.js
- index.js

Logical hierarchy between files:

  • index.js
    • ./reducers/index.js
      • ./reducers/todos.js
      • ./reducers/visibilityFilter.js
    • ./components/App.js
      • ./containers/AddTodo.js
        • ./actions/index.js - addTodo
      • ./containers/VisibleTodoList.js
        • ./actions/index.js - toggleTodo
        • ./components/TodoList.js
      • ./components/Footer.js
        • ./containers/FilterLink.js
          • ./actions/index.js - setVisibilityFilter
          • ./components/Link.js

The following is a detailed analysis, expanded from index.js, and then expanded layer by layer:

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

const store = createStore(reducer)

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

Since it is redux, all data should be stored in the store:
const store = createStore(reducer)
Different from the counter example, the Provider is used here. It brings a store property in which all component s can access the contents of its store.

<Provider store={store}>
  <App />
</Provider>

It's a little abstract just to see here, so what is < app / >?

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

It's still a little abstract. In terms of < visibletodolist / > expansion:

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

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)
    default:
      throw new Error('Unknown filter: ' + filter)
  }
}

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

const mapDispatchToProps = {
  onTodoClick: toggleTodo
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

export default VisibleTodoList

Contraction function body:

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

const getVisibleTodos = (todos, filter) => {}
const mapStateToProps = (state) => ({
  todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
const mapDispatchToProps = {
  onTodoClick: toggleTodo
}
const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)
export default VisibleTodoList

Have you seen the key points? The key is:

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

connect associates the format specific functions mapStateToProps and mapDispatchToProps with TodoList.
Expand TodoList and toggleTodo to see:

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

TodoList.propTypes = {
  todos: PropTypes.arrayOf(PropTypes.shape({
    id: PropTypes.number.isRequired,
    completed: PropTypes.bool.isRequired,
    text: PropTypes.string.isRequired
  }).isRequired).isRequired,
  onTodoClick: PropTypes.func.isRequired
}
export const toggleTodo = (id) => ({
  type: 'TOGGLE_TODO',
  id
})

From this, you can see mapStateToProps and mapDispatchToProps and their corresponding relationships.
mapStateToProps accessed todos in TodoList.propTypes. mapDispatchToProps accesses onTodoClick in TodoList.propTypes and indirectly calls reducer. Here, toggleTodo corresponds to todo.js:

    case 'TOGGLE_TODO':
      return state.map(todo =>
        (todo.id === action.id) 
          ? {...todo, completed: !todo.completed}
          : todo
      )

Readers can study < addtodo / > and < footer / > on their own.

You can directly go to the official demo address to view the code and display effect: codesandbox todos

Tags: React github

Posted on Mon, 04 May 2020 00:11:01 -0700 by kumar_ldh