Prev: Redux学习笔记-Vol.1-介绍

Action

Action是把数据从应用传到store的有效载荷。它是store数据的唯一来源,一般通过store.dispatch()将action传到store。
举个栗子:

const ADD_TODO = 'ADD_TODO';

//一个action可以表达为:
{
    type: ADD_TODO,
    text: 'Build my first Redux app'
}

说白了,action就是一个普通的javascript对象,但是有一点要注意:约定这个表示action的对象必须有一个type字段,来表示将要执行的动作。
尽量减少在action中传递数据

Action Creator

Action Creator就是生成action的方法。

function addTodo(text){
    return {
        type: 'ADD_TODO',
        text
    }
}

在Redux中,只需把action creator的结果返回给dispatch()即可发起一次dispatch过程。

dispatch(addTodo(text));

或者,创建一个被绑定的action creator来自动dispatch:

const boundAddTodo = (text) => dispatch(addTodo(text));
boundAddTodo();

目前为止,我们写好了一个action.js

//action type
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';


//其它常量
export const VisibilityFilters = {
    SHOW_ALL: 'SHOW_ALL',
    SHOW_COMPLETED: 'SHOW_COMPLETED',
    SHOW_ACTIVE: 'SHOW_ACTIVE'
};


//action creator
export function addTodo(text){
    return {
        type: ADD_TODO,
        text
    }
}

export function toggleTodo(index){
    return {
        type: TOGGLE_TODO,
        index
    }
}

export function setVisibilityFilter(filter){
    return {
        type: SET_VISIBILITY_FILTER,
        filter
    }
}

Reducer

有了action以后,现在需要reducer来指明如何更新state。

State结构

要明确的一点是,在Redux应用中,所有的state都被保存在一个单一的对象中。
举个栗子,一个todo应用,需要保存两种不同的数据

  • 当前选中的任务过滤条件

  • 完整的任务列表

{
    visiibilityFilter: 'SHOW_ALL',
    todos: [
        {
            text: 'Consider using Redux',
            complete: true
        },
        {
            text: 'Keep all state in a single tree',
            complete: false
        }
    ]
}

处理action

Reducer是一个纯函数,接受旧的state和action,返回新的state,形如:

    (previousState, action) => newState

保持reducer纯净非常重要,永远不要在reducer中做这些操作:

  • 修改传入的参数

  • 执行有副作用的操作,如API请求和路由跳转

  • 调用非纯函数,如Date.now()Math.random()

一个纯净的reducer是什么样的呢?
只要传入的参数相同,返回计算得到的下一个state就一定相同。
好,开始写reducer。

import { VisibilityFilters } from './actions';

const initialState = {
    visibilityFilter: VisibilityFilter.SHOW_ALL,
    todo: []
};

function todoApp(state = initialState, action){
    switch (action.type){
        case SET_VISIBILITY_FILTER:
            return  Object.assign({}, state, {
                visibilityFilter: action.filter
            });
        default:
            return state;
    }
}

注意:

  1. 不要修改state。上面的代码中只是使用Object.assign()创建了一个副本。

  2. default的情况下,返回旧的state在未知的情况下,一定要返回旧的state!

处理多个action

先增加两个ADD_TODOTOGGLE_TODO

case ADD_TODO:
    return Object.assign({}, state, {
        todos: [
            ...state.todos,//ES6大法好
            {
                text: action.text,
                complete: false
            }
        ]
    });
    
case TOGGLE_TODO:
    return Object.assign({}, state, {
        todos: state.todos.map(function(todo, index){
            if (index === action.index){
                return Object.assign({}, todo, {
                    completed: !todo.completed;
                });
            }
            return todo;
        });
    });

拆分reducer

function todos(state = [], action){
    switch(action.type){
        case ADD_TODO:
            return [
                ...state,
                {
                    text: action.text,
                    completed: false
                }
            ];
        case TOGGLE_TODO:
            return state.map(function(todo, index){
                if (index === action.index){
                    return Object.assign({}, todo, {
                        completed: !todo.completed
                    });
                }
                return todo;
            });
    }
}

function todoApp(state = initialState, action){
    switch(action.type){
        case SET_VISIBILITY_FILTER:
            return Object.assign({}, state, {
                visibilityFilter: action.filter
            });
        case ADD_TODO:
        case TOGGLE_TODO:
            return Object.assign({}, state, {
                todos: todos(state.todos, action)
            });
        default:
            return state;
    }
}

todos依旧接受state,但是这里的state变成了一个数组,todoApp只把需要更新的那一部分state传给todos。这就是reducer合成,是开发Redux应用最基础的模式。
用同样的方法把visibilityFilter拆分出来:

function visibilityFilter(state = SHOW_ALL, action){
    switch(action.type){
        case SET_VISIBILITY_FILTER:
            return action.filter;
        default:
            return state;
    }
}

然和修改总的reducer

function todoApp(state = {}, action){
    return {
        visibilityFilter: visibilityFilter(state.visibilityFilter, action),
        todos: todos(state.todos, action)
    };
}

合并的事,就交给Redux帮你来做:

import { combineReducers } from 'redux';

const todoApp = combineReducers({
    visibilityFilter,
    todos
});

export default todoApp;

ES6大法好
combineReducers接受的是一个对象,返回一个函数。由此,我们可以把所有的顶级的reducer放到一个独立文件中,通> > 过export暴露出每个reducer函数,然后用import * as reducer引入一个以他们名字作为key的Object:

import { combineReducers } from 'redux';
import * as reducer from './reducers';

const todoApp = combineReducers(reducer);
//ES6大法好!

截至目前,我们得到了一个完整的reducers.js

import { combineReducers } from 'redux';
import { ADD_TODO, TOGGLE_TODO, SET_VISIBILITY_FILTER, VisibilityFilters } from './actions';

function visibilityFilters(state = SHOW_ALL, action){
    switch(action.type){
        case SET_VISIBILITY_FILTER:
            return action.filter;
        default:
            return state;
    }
}

function todos(state = [], action){
    switch(action.type){
        case ADD_TODO:
            return [
                ...state,
                {
                    text: action.text,
                    completed: false
                }
            ];
        case TOGGLE_TODO:
            return state.map(function(todo, index){
                if (index === action.index){
                    return Object.assign({}, todo, {
                        completed: !todo.completed
                    });
                }
                return todo;
            });
        default:
            return state;
    }
}

const todoApp = combineReducer({
    visibilityFilters,
    todos
});

export default todoApp;

Store

在学Store之前,我们先来回顾一下actionreducer

  • Action:用来表示“发生了什么”

  • Reducer:根据“发生了什么”来决定如何更新state

现在需要知道的是,Store的作用就是上述两者联系起来。
关于Store的职责:

  • 维持应用的state

  • getState()方法用来获取state

  • dispatch(action)用来分发action,进而更新state

  • subscribe(listener)注册reducer,并返回一个取消注册的函数

FYI:Redux只有一个单一的Store!当逻辑复杂需要拆分的时候,请对Reducer下手,科科。
Now,我们来根据已经写好了的reducer来创建store,so easy~

import { createStore } from 'redux';
import todoApp from './reducers';

let store = createStore(todoApp);

createStore()方法可以的第二个参数是可选的,用于设置state的初始状态。

let store = createStore(todoApp, initialState);

发起actions

现在我们已经有了一个store,来看一下怎么用。

import { addTodo, toggleTodo } from './actions';

//获得state
store.getState();

//注册(订阅),state没触发一次更新,打印之
let unsubscribe = store.subscribe(function(){
    console.log(store.getState());
});

//发起action
store.dispatch(addTodo('finish your resume.'));
store.dispatch(toggleTodo(0));

//停止监听state更新
unsubscribe();  

看看这一part做了什么 ↓

//创建了一个store(约定store只有一个!)
import { createStore } from 'redux';
import todoApp from './reducers';

let store = createStore(todoApp);

严格的单向数据流(Redux核心)

  1. 发起:store.dispatch(action)

  2. 传递:store将发起的action和当前的state传递给根reducer,根reducer再将这两个参数传递给子reducer们;

  3. 更新:通过了子reducer们的各司其职以后,根reducer把他们的输出合并为一个单一的state树;

  4. 保存:store将这个reducer返回的state保存起来,这一次的数据流结束。


劉凯里
841 声望1.6k 粉丝

我可能是一个假前端