[Redux] Colocating Selectors with Reducers

We will learn how to encapsulate the knowledge about the state shape in the reducer files, so that the components don’t have to rely on it.

In current VisibleTodoList.js:

import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { toggleTodo } from '../actions';
import TodoList from './TodoList';

const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'all':
      return todos;
    case 'completed':
      return todos.filter(t => t.completed);
    case 'active':
      return todos.filter(t => !t.completed);
    default:
      throw new Error(`Unknown filter: ${filter}.`);
  }
};

const mapStateToProps = (state, { params }) => ({
  todos: getVisibleTodos(state.todos, params.filter || 'all'),
});

const VisibleTodoList = withRouter(connect(
  mapStateToProps,
  { onTodoClick: toggleTodo }
)(TodoList));

export default VisibleTodoList;

Currently, the getVisibleTodos(state.todos), depends on state's structure.

Move getVisibleTodos to reducer file:

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 = [{
  id: 0,
  text: "ok",
  completed: false
}], 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;
  }
};

export default todos;


export const getVisibleTodos = (state, filter) => {
  switch (filter) {
    case 'all':
      return state;
    case 'completed':
      return state.filter(t => t.completed);
    case 'active':
      return state.filter(t => !t.completed);
    default:
      throw new Error(`Unknown filter: ${filter}.`);
  }
};

Then in the RootReducer, we manage the state:

import { combineReducers } from 'redux';
import todos, * as fromTodos from './todos';

const todoApp = combineReducers({
  todos
});

export default todoApp;

export const getVisibleTodos = (state, filter) =>
    fromTodos.getVisibleTodos(state.todos, filter);

Use it in VisibleTodoList.js:

import {connect} from 'react-redux';
import {toggleTodo} from '../actions';
import TodoList from './TodoList';
import {withRouter} from 'react-router';
import { getVisibleTodos } from '../reducers';


const mapStateToProps = (state, {params}) => {
    return {
        todos: getVisibleTodos(state, params.filter || 'all'), // if filter is '' then change to 'all'
    };
};

const VisibleTodoList = withRouter(connect(
    mapStateToProps,
    {onTodoClick: toggleTodo}
)(TodoList));

export default VisibleTodoList;
原文地址:https://www.cnblogs.com/Answer1215/p/5565084.html