[Redux] Normalizing the State Shape

We will learn how to normalize the state shape to ensure data consistency that is important in real-world applications.

We currently represent the todos in the state free as an array of todo object. However, in the real app, we probably have more than a single array and then todos with the same IDs in different arrays might get out of sync.

const byIds = (state = {}, action) => {
  switch (action.type) {
    case ‘ADD_TODO‘:
    case ‘TOGGLE_TODO‘:
      return {
        ...state,
        [action.id]: todo(state[action.id], action),
      };
    default:
      return state;
  }
};

For using object spread, we need to include plugins:

// .baberc

{
  "presets": ["es2015", "react"],
  "plugins": ["transform-object-rest-spread"]
}

Anytime the ByID reducer receives an action, it‘s going to return the copy of its mapping between the IDs and the actual todos with updated todo for the current action. I will let another reducer that keeps track of all the added IDs.

const allIds = (state = [], action) => {
  switch(action.type){
    case ‘ADD_TODO‘:
      return [...state, action.id];
    default:
      return state;
  }
};

the only action I care about is a todo because if a new todo is added, I want to return a new array of IDs with that ID as the last item. For any other actions, I just need to return the current state.

Finally, I still need to export the single reducer from the todos file, so I‘m going to use combined reducers again to combine the ByID and the AllIDs reducers.

const todos = combineReducers({
  allIds,
  byIds
});

export default todos;

Now that we have changed the state shape in reducers, we also need to update the selectors that rely on it. The state object then get visible todos is now going to contain ByID and AllIDs fields, because it corresponds to the state of the combined reducer.

const getAllTodos = (state) => {
  return state.allIds.map( (id) => {
    return state.byIds[id];
  })
};

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

My todos file has grown quite a bit so it‘s a good time to extract the todo reducer that manages just when you go todo into a separate file of its own. I created a file called todo in the same folder and I will paste my implementation right there so that I can import it from the todos file.

// reducers/todo.jsconst 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;
  }
};

------------------

//todos.js

import { combineReducers } from ‘redux‘;
import  todo  from ‘./todo‘;

const byIds = (state = {}, action) => {
  switch (action.type) {
    case ‘ADD_TODO‘:
    case ‘TOGGLE_TODO‘:
      return {
        ...state,
        [action.id]: todo(state[action.id], action),
      };
    default:
      return state;
  }
};

const allIds = (state = [], action) => {
  switch(action.type){
    case ‘ADD_TODO‘:
      return [...state, action.id];
    default:
      return state;
  }
};

const todos = combineReducers({
  allIds,
  byIds
});

export default todos;

const getAllTodos = (state) => {
  return state.allIds.map( (id) => {
    return state.byIds[id];
  })
};

export const getVisibleTodos = (state, filter) => {
  const allTodos = getAllTodos(state);
  console.log(allTodos);
  switch (filter) {
    case ‘all‘:
      return allTodos;
    case ‘completed‘:
      return allTodos.filter(t => t.completed);
    case ‘active‘:
      return allTodos.filter(t => !t.completed);
    default:
      throw new Error(`Unknown filter: ${filter}.`);
  }
};
//todo.js
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;
    }
};

export default todo;
时间: 2024-10-25 18:53:34

[Redux] Normalizing the State Shape的相关文章

[Redux] Persisting the State to the Local Storage

We will learn how to use store.subscribe() to efficiently persist some of the app’s state to localStorage and restore it after a refresh. To save data in to localStroge,  we first create a localStorgejs file and two methods, one for get and one for s

[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

Redux生态系统

生态系统 Redux 是一个体小精悍的库,但它相关的内容和 API 都是精挑细选的,足以衍生出丰富的工具集和可扩展的生态系统. 如果需要关于 Redux 所有内容的列表,推荐移步至 Awesome Redux.它包含了示例.样板代码.中间件.工具库,还有很多其它相关内容.要想学习 React 和 Redux ,React/Redux Links 包含了教程和不少有用的资源,Redux Ecosystem Links 则列出了 许多 Redux 相关的库及插件. 本页将只列出由 Redux 维护者

React loves you — 洞悉Redux装的逼

你若装逼,请带我飞! 从前,从前,听说React只负责UI,话说写Angular代码就像写后端,现在看来,React赢在情怀上了: 我认为没必要老是拿React和Angular做比较,Angular是一套大而全的相对完备的框架:而React确实是只负责UI,只是多出很多概念层的东西,需要自己以此去构造:React更像是打造一个由React作为主线的生态:以component为基础,虚拟DOM解决性能瓶颈,单向数据流统一管理components,webpack.ES6.JSX完美融合,还有Flux

【前端,干货】react and redux教程学习实践(二)。

前言 这篇博文接 [前端]react and redux教程学习实践,浅显易懂的实践学习方法. ,上一篇简略的做了一个redux的初级demo,今天深入的学习了一些新的.有用的,可以在生产项目中使用的前端架构,我将尽量以最简单的语言描述,如果有童鞋看不懂,也可以私下问我. 复习 前一节我们已经知道,一个redux应用,主要有几个概念,它们的共同作用都是管理一个全局state,使react组件的state集中处理,想一下你在写react组件的时候,组件的state总是或多或少与父级组件有关联,一般

redux使用过程中遇到的两个致命的关键点

一.在reducer中,返回的state必须是全新的对象,否则,redux不会执行listening方法,因为redux会认为state没有更新过,没必要重新渲染view. 出现问题的例子: const user=(state={name='',age=0},action)=>{ switch(action.type){ case 'CHANGE_NAME': state.name='zhangsan';//在原object中修改name return state; default: retur

对redux的理解

 redux原理 某公司有物流(actionType).电商(actionType).广告(actionType)3块业务,在公司财务系统(state)统一记录着三块业务分别赚取到的资金.某天,电商业务在公司电商平台销售了价值100w的商品(action),赚取到的资金通过发票(dispatch)的形式送到业务的财务部门,财务部门通过自己业务块,计算赚取到的利润(reducer),再更新到财务系统(state). 核心原理: 通过某个事件action把结果通过dispatch发送到reducer

Redux介绍

Redux 有三个基本的原则: 1,单一状态树,redux 只使用一个javascript 对象来保存整个应用的状态. 状态树样式如下: const state = { count: 0 } 2,状态是只读的,不是说不能修改state,只是我们不能直接修改state, 那么怎样才要可以改state呢? 修改state的唯一方法是发送一个action,action也很简单,就是一个JavaScript 对象,它描述了我们将要对state作什么样的变化. 如下: { type: 'MINUS' }

React & Redux 的一些基本知识点

一.React.createClass 跟 React.Component 的区别在于后者使用了ES6的语法,用constructor构造器来构造默认的属性和状态. 1. React.createClass import React from 'react';       const Contacts = React.createClass({             render() {                return (                    <div><