Redux-ORM理解之实现Todo List

算法溯光者
• 阅读 2613

Redux-ORM理解之实现Todo List

一、概念

  • redux-orm及其作用:

redux-orm主要是用来管理我们的state数据,当一个项目比较大,逻辑结构比较复杂,每个数据之间都有联系,此时便需要将这些state进行统一管理,redux-orm就是用来解决这些数据间的关联问题,所以redux-orm就像一个关系型数据库,而每个对象类型就像是数据库中的数据表,并且是以JavaScript的对象形式存储这些数据的。
那么也就是说,当项目的中涉及到的对象类型并不很多,且对象类型之间的关联性不大的时候,并不建议使用redux-orm,如果项目足够简单,连redux也不需要使用到。

二、实现Todo List及代码解析

1、分析实例及行为

在todo list 实现的基本功能有:
选择用户
创建todo
标记todo完成
删除todo
创建tag标签
移除tag标签
所以按照上面的功能,可以将todo list分为三张表,分别是user(用户表)、todo表和tag标签表,这些表就相当于一个实例,每个实例都会有自己的行为属性和方法

  • User:

属性:id、 name
行为:selectUser

  • Todo:

属性:id、text(todo内容)、done(标记结束)、user、 tags
行为: createTodo、markDone、deleteTodo

  • Tag:

属性: name
行为: addTagToTodo、 removeTagFromTodo

2、创建state

State是一个对象,是用来描述应用。在这个todo list中,当需要有一个对象用来描述

3、Action创建函数

Action是把数据从应用传到store的有效载荷,它是store数据的唯一来源。其本质上是JavaScript普通对象,action内必须使用type字符串类型,type表示将要执行的动作,在多数情况下,type被已定义为字符串常量。
Action创建函数就是生成action方法,该方法只是简单的返回一个action。例如,对于添加标签这个action是这样执行的,先创建一个addTagToTodo的方法,该方法接收tag和todo两个参数,然后将其返回为一个对象,其中对象的属性值中必须有一个type

export const addTagToTodo = (todo, tag) => {
    return {
        type: ADD_TAG_TO_TODO,
        payload: {
            todo,
            tag,
        },
    };
};
4、构建数据表并关联

在ORM中定义了一个Model类来将实体类关联起来, 对于Todo这个实体类来说,通过ES6类语法进行定义,并继承了ORM的Model类。
Model需要设置名称才可以识别到对应的数据,以及实体类与其他数据关联的方式。
对于Todo类来说,它跟User只能是一对多,一个User下有多个Todo,而跟tag可以多对多,一个Todo可以有多个tag,通用一个tag可以存在在多个Todo中,所以Model类中的fields主要是用了来定义当前实体类与其他类的关联关系,这些如:fkmanyoneToOne接收两个参数,第一个是被关联的实体类,就是在Model中定义的modelName和操作的类名。 而且关联关系只需要定义一次就可以了,不需要重复定义。
最后要实现实体类间的关联必须将其注册到Schema()方法中并导出,这样才能够真正关联其实体间的关系。当实体类比较多的情况下,可能需要一个单独的文件来存放这些需要注册的实体类,这样可以使项目模块化更易于管理。

import { Schema, Model, many, fk } from 'redux-orm';
import {
    CREATE_TODO,
    MARK_DONE,
    DELETE_TODO,
    ADD_TAG_TO_TODO,
    REMOVE_TAG_FROM_TODO,
} from './actions';   

export class Todo extends Model {
    static reducer(state, action, Todo, session) {
        const { payload, type } = action;
        switch (type) {
        case CREATE_TODO:
            const tagIds = action.payload.tags.split(',').map(str => str.trim());
            const props = Object.assign({}, payload, { tags: tagIds, done: false });
            Todo.create(props);
            break;
        case MARK_DONE:
            Todo.withId(payload).done = true;
            break;
        case DELETE_TODO:
            Todo.withId(payload).delete();
            break;
        case ADD_TAG_TO_TODO:
            Todo.withId(payload.todo).tags.add(payload.tag);
            break;
        case REMOVE_TAG_FROM_TODO:
            Todo.withId(payload.todo).tags.remove(payload.tag);
            break;
        }
    }
}
Todo.modelName = 'Todo';
Todo.fields = {
    tags: many('Tag', 'todos'),
    user: fk('User', 'todos'),
};

export const schema = new Schema();
schema.register(Todo);

export default schema;
5、更新状态

Reducers主要是用来更新state,响应actions并发送到store的引用状态变化。
在Redux应用中,所有的state都被保存在一个单一对象中。
reducer是一个纯函数,接收旧的state和action,返回新的state。不能够在reducer中传递参数、执行有副作用的操作以及调用非纯函数。只需单纯地执行计算就可以,其更新是局部的,只有当当前的reducer中的数据发生改变后,reducer才会重新进行计算。
在Redux-ORM中使用特定reducers的模型来操作数据。首先在Model类中先定义一个静态的reducer方法,它会接收所有需要更新的action。如果没有定义reducers的话,ORM会直接使用默认方法去实现更新。
其原理是,在静态的reducer方法中接收四个参数:state(状态)、action(当前操作)、Todo(模型类)、session(ORM的会话实例)。在Todo的reducer中,通过对当前Todo的type进行循环遍历来执行对Todo的增加、删除、修改等操作,而session这个会话实例参数主要是用来访问和查询其他Model,但是不建议在当前的Model修改其他Model中的数据。

6、数据的筛选

在Redux-ORM中的seletors是使用了了第三方插件reselect
selector在ORM中可以计算派生数据,其在整个ORM中一直是有效的,跟reducer一样,只要其接收的参数发生改变就会触发该方法,而且selector是可组合的,可以作为其他seletor的输入

export const todos = createSelector(
    ormSelector,
    state => state.selectedUserId,
    schema.createSelector((orm, userId) => {
        return orm.Todo.withRefs.filter({ user: userId }).map(todo => {
            const obj = Object.assign({}, todo.ref);
            obj.tags = todo.tags.withRefs.map(tag => tag.name);
            return obj;
        });
    })
);  

首先使用createSelector方法创建了todo的选择器,该方法中的第一个参数始终是orm的selector,然后对input selector进行回调,回调是用过schema创建了createSelector方法来计算关联的数据然后并返回。
在todo的seletor中,通过与selectedUserId中进行关联,当被选中的userId发生变化时就会触发该方法,并过滤出该user下的所有Todo数据及该Todo下的tag数据并返回。

7、应用在视图中

当我们将所有的数据都定义好了之后,就需要在视图中去使用
首先,在入口文件导入redux中的createStore、combineReducers、applyMiddleware、Provider还有redux中的createLogger等方法
createStore:该函数主要是用来生成store, store就是保存数据的地方,相当于一个容器,整个项目中只能有一个store
combineReducer:主要是用于Reducer的拆分,其可以将拆分的各个子reducer函数通过该方法合成一个大的Reducer
applyMiddlewarecreateLogger都是redux的中间件,主要是用来执行异步操作。其中createLoggerredux-logger模块中的方法,是生成redux日志,并打印在控制台,该方法是放在applyMiddleware方法中。
Provider: 主要是让Store容器组件拿到state。
最后,可以使用redux中的connect()方法将UI组件和store容器组件连接起来,主要依靠输入与输出来实现。
输入(mapStateToProps)是将store中的数组转化为UI组件的参数。 其是一个函数,建立一个从state对象到UI组件props对象的映射关系,其执行后返回一个对象,里面的每个键值对就是一个映射
输出(mapDispatchToProps)是用户发出的动作转变成Action对象,从UI组件传出去。它定义哪些用户的操作应该当做Action并传给Store,它个可以是一个函数,也可以是一个对象。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
3年前
sql注入
反引号是个比较特别的字符,下面记录下怎么利用0x00SQL注入反引号可利用在分隔符及注释作用,不过使用范围只于表名、数据库名、字段名、起别名这些场景,下面具体说下1)表名payload:select\from\users\whereuser\_id1limit0,1;!(https://o
Stella981 Stella981
3年前
List的Select 和Select().tolist()
List<PersondelpnewList<Person{newPerson{Id1,Name"小明1",Age11,Sign0},newPerson{Id2,Name"小明2",Age12,
Wesley13 Wesley13
3年前
Java爬虫之JSoup使用教程
title:Java爬虫之JSoup使用教程date:201812248:00:000800update:201812248:00:000800author:mecover:https://imgblog.csdnimg.cn/20181224144920712(https://www.oschin
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Stella981 Stella981
3年前
ELK学习笔记之ElasticSearch的索引详解
0x00ElasticSearch的索引和MySQL的索引方式对比Elasticsearch是通过Lucene的倒排索引技术实现比关系型数据库更快的过滤。特别是它对多条件的过滤支持非常好,比如年龄在18和30之间,性别为女性这样的组合查询。倒排索引很多地方都有介绍,但是其比关系型
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(