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.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;
}
};

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

//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;

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

  1. 关于props和state以及redux中的state

    React的数据模型分为共有数据和私有数据,共有数据可以在组件间进行传递,私有数据为当前组件私有.共有数据在React中使用props对象来调用,它包含标签所有的属性名称和属性值,props对象有三个 ...

  2. [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 localSt ...

  3. [Functional Programming ADT] Create a Redux Store for Use with a State ADT Based Reducer

    With a well defined demarcation point between Redux and our State ADT based model, hooking up to a R ...

  4. 前端(十一):props、state及redux关系梳理

    所谓状态机,是一种抽象的数据模型,是“事物发展的趋势”,其原理是事件驱动.广泛地讲,世界万物都是状态机. 一.状态机是一种抽象的数据模型 在react中,props和state都可以用来传递数据.这里 ...

  5. Redux的State不应该全部放在Store里

    使用了redux管理应用的状态,应用的状态不应该全部放在Store里面. 前端状态主要有一下两种: 1. Domain data 2. UI State 1. Domain data 来自于服务端对领 ...

  6. [Redux] Colocating Selectors with Reducers

    We will learn how to encapsulate the knowledge about the state shape in the reducer files, so that t ...

  7. 详解 Node + Redux + MongoDB 实现 Todolist

    前言 为什么要使用 Redux? 组件化的开发思想解放了繁琐低效的 DOM 操作,以 React 来说,一切皆为状态,通过状态可以控制视图的变化,然后随着应用项目的规模的不断扩大和应用功能的不断丰富, ...

  8. redux的源码解析

    一. redux出现的动机 1. Javascript 需要管理比任何时候都要多的state2. state 在什么时候,由于什么原因,如何变化已然不受控制.3. 来自前端开发领域的新需求4. 我们总 ...

  9. [Redux] Important things in Redux

    Root Smart component can be overloaded, divide 'smart' component wisely & using Provider. Proble ...

随机推荐

  1. 移动web HTML5使用photoswipe模仿微信朋友圈图片放大浏览

    先来几张效果图: 点击其中一张照片可放大,可支持图片文字描述: 同时支持分享功能: 支持手势放大缩小 使用js框架是PhotoSwipe. PhotoSwipe是一个图片放大插件,兼容pc和移动端,经 ...

  2. servlet 默认是线程安全的吗?

    由于Servlet默认是以多线程模式执行的,所以,在编写代码时需要非常细致地考虑多线程的安全问题.然而,很多人编写Servlet程序时并没 有注意到多线程安全的问题,这往往造成编写的程序在少量用户访问 ...

  3. OneAlert 入门(三)——事件分析

    OneAlert 是国内首个 SaaS 模式的云告警平台,集成国内外主流监控/支撑系统,实现一个平台上集中处理所有 IT 事件,提升 IT 可靠性.有了 OneAlert,你可以更快更合理地为事件划分 ...

  4. 在安全层面,企业如何获得更好的投资回报率 ROI?

    前言 任何企业对投资都有回报的要求,回报可能是直接的「利润」,达到短期.长期的目标,或者通过投资减少损失.因此每个项目的决策者在每笔投资前都要衡量 ROI,证明该投资能达到的效果和收益,以便在项目结束 ...

  5. 移动应用产品开发-android开发项目记录

    今天主要在做app前端开发,做了一个资讯列表和资讯详情,主要模仿网易新闻来做,页面布局简单,但java代码和实现比较麻烦 毕竟才开始做,研究的不那么透彻,也不是很熟练 用心去做,专注开发,今天也算作出 ...

  6. javascript中通过replace函数搜索和替换指定字符串

    javascript中我们可以通过replace函数替换部分字符串为指定字符串,本文展示了replace的详细用法,并且通过范例演示了如何进行部分替换.完整替换和不区分大小写替换. javascrip ...

  7. iOS开发网络篇—多线程断点下载

    iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始, ...

  8. Centos6.4 为用户添加sudo功能

    sudo即super user do,以超级管理员的方式运行命令.使用时,只需在命令最前面加上sudo即可. 要为用户添加sudo功能,需要修改sudo的配置文件: vi /etc/sudoers ( ...

  9. MD5Helper辅助类

    DES加密和解密 public class MD5Helper { ///DES加密 ///sKey public string MD5Encrypt(string pToEncrypt, strin ...

  10. [GRYZ2014]迷宫问题

    设有一个N*N方格的迷宫,入口和出口分别在左上角和右上角,迷宫格子中分别放有0和1,0表示可走,1表示不能走,迷宫走的规则如图.当迷宫给出之后,找出一条从入口到出口的通路. 输入:N N*N的迷宫 输 ...