Redux-ORM理解之實現Todo List

Redux-ORM理解之實現Todo List

1、概念

  • redux-orm及其做用:

redux-orm主要是用來管理咱們的state數據,當一個項目比較大,邏輯結構比較複雜,每一個數據之間都有聯繫,此時便須要將這些state進行統一管理,redux-orm就是用來解決這些數據間的關聯問題,因此redux-orm就像一個關係型數據庫,而每一個對象類型就像是數據庫中的數據表,而且是以JavaScript的對象形式存儲這些數據的。
那麼也就是說,當項目的中涉及到的對象類型並不不少,且對象類型之間的關聯性不大的時候,並不建議使用redux-orm,若是項目足夠簡單,連redux也不須要使用到。git

2、實現Todo List及代碼解析

一、分析實例及行爲

在todo list 實現的基本功能有:
選擇用戶
建立todo
標記todo完成
刪除todo
建立tag標籤
移除tag標籤
因此按照上面的功能,能夠將todo list分爲三張表,分別是user(用戶表)、todo表和tag標籤表,這些表就至關於一個實例,每一個實例都會有本身的行爲屬性和方法github

  • User:

屬性:id、 name
行爲:selectUser數據庫

  • Todo:

屬性:id、text(todo內容)、done(標記結束)、user、 tags
行爲: createTodo、markDone、deleteTodoredux

  • Tag:

屬性: name
行爲: addTagToTodo、 removeTagFromTodo數組

二、建立state

State是一個對象,是用來描述應用。在這個todo list中,當須要有一個對象用來描述session

三、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,
        },
    };
};
四、構建數據表並關聯

在ORM中定義了一個Model類來將實體類關聯起來, 對於Todo這個實體類來講,經過ES6類語法進行定義,並繼承了ORM的Model類。
Model須要設置名稱才能夠識別到對應的數據,以及實體類與其餘數據關聯的方式。
對於Todo類來講,它跟User只能是一對多,一個User下有多個Todo,而跟tag能夠多對多,一個Todo能夠有多個tag,通用一個tag能夠存在在多個Todo中,因此Model類中的fields主要是用了來定義當前實體類與其餘類的關聯關係,這些如:fkmanyoneToOne接收兩個參數,第一個是被關聯的實體類,就是在Model中定義的modelName和操做的類名。 並且關聯關係只須要定義一次就能夠了,不須要重複定義。
最後要實現實體類間的關聯必須將其註冊到Schema()方法中並導出,這樣纔可以真正關聯其實體間的關係。當實體類比較多的狀況下,可能須要一個單獨的文件來存放這些須要註冊的實體類,這樣可使項目模塊化更易於管理。app

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;
五、更新狀態

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中的數據。異步

六、數據的篩選

在Redux-ORM中的seletors是使用了了第三方插件reselect
selector在ORM中能夠計算派生數據,其在整個ORM中一直是有效的,跟reducer同樣,只要其接收的參數發生改變就會觸發該方法,並且selector是可組合的,能夠做爲其餘seletor的輸入ide

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數據並返回。

七、應用在視圖中

當咱們將全部的數據都定義好了以後,就須要在視圖中去使用
首先,在入口文件導入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,它個能夠是一個函數,也能夠是一個對象。

相關文章
相關標籤/搜索