中组件间通讯的数据流是单向的,你能够在读书官方文书档案在此以前和事后阅读本文

http://gaearon.github.io/redux/index.html
,文档在
http://rackt.github.io/redux/index.html
。本文不是合瑞典语档的翻译。你能够在读书官方文书档案在此以前和未来阅读本文,以加深个中的显要概念。

React

在 React 中,UI 以组件的款型来搭建,组件之间能够嵌套组合。另,React
中组件间通讯的数据流是单向的,顶层组件可以因此 props
属性向下层组件传递数据,而下层组件不可能向上层组件传递数据,兄弟组件之间平等不能。那样轻松的单向数据流支撑起了
React 中的数据可控性。

那么,更宏观的零部件间通讯情势该怎么落到实处吗?

   1. 嵌套组件间,上层组件向下层组件传递回调函数,下层组件触发回调来更新上层组件的数据。

   2. 以事件的形式,使用发布订阅的方式来通知数据更新。

   3. Flux —- Fackbook 提出的管理 React 数据流的架构。Flux 不像一个框架,更是一种组织代码的推荐思想。就像 “引导数据流流向的导流管”。

   4. 其他的 “导流管”。ReFlux,Redux 等。

前三种样式其实也丰富在小应用中跑起来。但当项目更是大的时候,管理数据的轩然大波或回调函数将尤为多,也将尤为不好管理了。
对于后三种样式,个人通过比照后,能够看到 Redux 对 Flux
架构的一对简化。如 Redux 限定多少个运用中不得不有纯粹的
store,那样的限量能够让使用中多少结果聚集国化工进出口总集团,提升可控性。当然,不止如此。

凭借该类型源码的习贯,示例都以依靠 ES二〇一四 的语法来写的。

Redux

Redux 首要分为四个部分 Action、Reducer、及 Store

Redux
是应用状态管理服务。即使本身遭到了
Flux
很深的熏陶,不过其主干概念却特别简单,便是 Map/Reduce 中的 Reduce。

Action

在 Redux 中,action 主要用来传递操作 State 的新闻,以 Javascript Plain
Object 的花样存在,如

{
  type: 'ADD_FILM',
  name: 'Mission: Impossible'
}

在上头的 Plain Object 中,type 属性是必备的,除了 type 字段外,action
对象的组织完全在于你,提出尽量轻便。type 一般用来注明管理 state
数据的方法。如下面的 ‘ADD_FILM’ 说明要追加一个影片。而 name
表明了增添这么些电影的影视名叫 ‘Mission:
Impossible’。那么,当大家必要表明扩大另一部影片时,就必要其余三个action,如

{
  type: 'ADD_FILM',
  name: 'Minions'
}

地点写法未有别的难点,但细想,当我们扩展的影视更增多的时候,那这种直白评释的
Plain Object 将更为多,不好组织。实际上,我们能够经过创办函数来生产
action,那类函数统称为 Action Creator,如

function addFilm(name) {
  return { type: 'ADD_FILM', name: name };
}

那般,通过调用 addFilm(name) 就能够获得相应的 Action,非常直白。

大家看一下 Javascript 中 Array.prototype.reduce 的用法:

Reducer

有了 Action
来传达须求操作的新闻,那么就需求有依照那一个新闻来做相应操作的不二等秘书技,那就是Reducer。 Reducer 一般为简易的管理函数,通过传播旧的 state 和提醒操作的
action 来更新 state,如

 function films(state = initialState, action) {
  switch (action.type) {

  case 'ADD_FILM':
// 更新 state 中的 films 字段
return [{
  id: state.films.reduce((maxId, film) => Math.max(film.id, maxId), -1) + 1,
  name: action.name
}, ...state];

  case 'DELETE_FILM':
return state.films.filter(film =>
    film.id !== action.id
);

  case 'SHOW_ALL_FILM':
return Object.assign({}, state, {
    visibilityFilter: action.filter
  });

  default:
return state;
 }

上面代码体现了 Reducer 根据传入的 action.type 来相称 case 进行不一致的
state 更新。

鲜明性,当项目中存在进一步多的 action.type 时,下边包车型地铁 films 函数( Reducer
)将变得更大,越多的 case
将招致代码非常不足明晰。所以在代码协会上,平时会将 Reducer 拆分成二个个小的
reducer,各样 reducer 分别管理 state
中的一有的数据,最后将拍卖后的多寡统一成为整个 state。

在上边的代码中,我们得以把 ‘ADD_FILM’ 和 ‘DELETE_FILM’ 归为操作
state.films 的类,而 ‘SHOW_ALL_FILM’ 为过滤显示类,所以能够把大的 film
Reducer 拆分成 filmReducer 和 filterReducer,如

const initState = '';
const actions = ['a', 'b', 'c'];
const newState = actions.reduce(
    ( (prevState, action) => prevState + action ),
    initState
);

1 filmReducer

function filmReducer(state = [], action) {
  switch (action.type) {
  case 'ADD_FILM':
  // 更新 state 中的 films 字段
  return [{
id: state.films.reduce((maxId, film) => Math.max(film.id, maxId), -1) + 1,
name: action.name
  }, ...state];
  case 'DELETE_FILM':
  return state.films.filter(film =>
  film.id !== action.id
  );
  default:
    return state;
  }
}

从 Redux 的角度来看,应用程序的景观类似于地方函数中的 initState
newState 。给定 initState 之后,随着 action
的值持续扩散给总结函数,获得新的 newState

2 filterReducer

function filterReducer(state, action) {
  switch (action.type) {
  case 'SHOW_ALL_FILM':
return Object.assign({}, state, {
visibilityFilter: action.filter
});
  default:
return state;
  }
}

聊到底,通过整合函数将方面三个 reducers 组合起来,如

function rootReducer(state = {}, action) {
  return {
films: filmReducer(state.films, action),
filter: filterReducer(state.filter, action)
  };
}

地方的 rootReducer 将差别部分的 state 传给对应的 reducer
管理,最后合并全部 reducer 的再次回到值,组成总体state。

实在,Redux 提供了 combineReducers() 方法来做 rootReducer
所做的作业。使用 combineReducers 来重构 rootReducer,如

var rootReducer = combineReducers({
films: filmReducer,
filter: filterReducer
  });

combineReducers() 将调用一文山会海 reducer,并依据对应的 key 来筛选出 state
中的一有个别数据给相应的 reducer,那样也象征每三个小的 reducer
将不得不处理 state 的一局地数据,如:filterReducer 将只可以管理及返回state.filter 的多少,假若急需利用到其他 state 数据,那依旧要求为那类
reducer 传入整个 state。

在 Redux 中,三个 action 能够触发五个 reducer,贰个 reducer
中也足以涵盖八种 action.type 的管理。属于多对多的关联。

那么些计算函数被称之为 Reducer,正是上例中的
(prevState, action) => prevState + action

Store

回顾 Action 及 Reducer:

Action 用来表达操作消息,Reducer 根据 Action 来更新 State。

在 Redux 项目中,Store 是单纯的。维护着一个大局的 State,并且根据 Action
来开始展览事件分发管理 State。能够看出 Store 是一个把 Action 和 Reducer
结合起来的对象。

Redux 提供了 createStore() 方法来 生产 Store,并提供多个 API,如

    var store = createStore(rootReducer);  // 其中 rootReducer 为顶级的 Reducer

store 对象足以省略的理解为如下格局

function createStore(reducer, initialState) {

//闭包私有变量 
var currentReducer = reducer;
var currentState = initialState;
var listeners = [];

function getState() {
  return currentState;
}

function subscribe(listener) {
  listeners.push(listener);

  return function unsubscribe() {
    var index = listeners.indexOf(listener);
    listeners.splice(index, 1);
  };
}

function dispatch(action) {
    currentState = currentReducer(currentState, action);
    listeners.slice().forEach(listener => listener());
    return action;
}

//返回一个包含可访问闭包变量的公有方法
return {
  dispatch,
  subscribe,
  getState
};
}

store.getState() 用来博取 state 数据。

store.subscribe(listener) 用于注册监听函数。每当 state
数据更新时,将会接触监听函数。

而 store.dispatch(action) 是用于将三个 action 对象发送给 reducer
举行拍卖。如

store.dispatch({
  type: 'ADD_FILM',
  name: 'Mission: Impossible'
}); 

store 对象使得大家可以透过 store.dispatch(action) 来压缩对 reducer
的直白调用,并且能够更加好地对 state 实行联合保管。未有 store,大概会油但是生
reducer(currentState, action) 那样的往往地传来 state 参数的立异情势。

Immutable State

Redux
认为,三个应用程序中,全体应用模块之间需求分享访谈的多寡,都应该放在
State 对象中。那些利用模块恐怕是指 React
Components,也说不定是你和睦访谈 AJAX API
的代理模块,具体是如何并未一定的限量。State 以 “树形”
的艺术保留应用程序的两样部分的数额。这么些多少可能源于于网络调用、本地数据库查询、以至席卷近年来有个别UI 组件的有时执长势况(只要是内需被分裂模块访谈)、以至当前窗口大小等。

Redux 没有明确用什么样格局来保存 State,可能是 Javascript 对象,或者是
Immutable.js
的数据结构。不过有有些,你最佳确认保证 State 中各种节点都以 Immutable
的,那样将保险 State
的主顾在认清数据是或不是变动时,只要简单地展开援用相比较就能够,举个例子:

newState.todos === prevState.todos

故而制止 Deep Equal 的遍历进度。

为了确定保障那一点,在你的 Reducer 中更新 State 成员必要如此做:

`let myStuff = [
    {name: 'henrik'}
]

myStuff = [...mystuff, {name: 'js lovin fool']`

myStuff 是一个全新的目的。

若是更新的是 Object ,则:

let counters = {
    faves: 0,
    forward: 20,
}
// this creates a brand new copy overwriting just that key
counters = {...counters, faves: counters.faves + 1}

而不是:

counters.faves = counters.faves + 1}

要制止对 Object 的 in-place editing。数组也是一模一样:

let todos = [
    { id: 1, text: 'have lunch'}
]
todos = [...todos, { id: 2, text: 'buy a cup of coffee'} ]

而不是:

let todos = [
    { id: 1, text: 'have lunch'}
]
todos.push({ id: 2, text: 'buy a cup of coffee'});

遵从那样的措施,无需
Immutable.js
你也足以让和睦的应用程序状态是 Immutable 的。

在 Redux 中,State 只好通过 action 来变更。Reducer 便是依照
action 的语义来造成 State 改换的函数。Reducer
的实行是共同的。在给定 initState 以及一多种的
actions,无论在什么时间,重复推行多少次 Reducer,都应当赢得一致的
newState。那使得你的应用程序的情事是足以被 Log 以及 Replay
的。这种显著,减弱了前端开辟所面临的繁杂气象的乱入难点。分明的处境、再增多Hot-Reloaidng 和对应的
Dev-Tool,使得前端选用的可控性大大进步了。

bindActionCreators

从上面的 Action 相关介绍中可见,大家采纳了 ActionCreator 来生产
action。所以在骨子里的 store.dispatch(action) 中,我们供给如此调用
store.dispatch(actionCreator(…args))。

以史为鉴 Store 对 reducer 的包裹(减弱传播 state 参数)。能够对
store.dispatch 进行再一层封装,将多参数转化为单参数的情势。 Redux 提供的
bindActionCreators 就做了那件事。如

var actionCreators = bindActionCreators(actionCreators, store.dispatch);

这段日子,经 bindActionCreators 包装过后的 action Creator 造成了独具改动全局
state
数据的七个函数,将那么些函数分发到各类地点,即能经过调用那几个函数来改换全局的
state。

State 结构划虚拟计

Redux (Flux) 都提出在保存 State
数据的时候,应该尽大概地遵循范式,防止嵌套数据结构。假若现身了嵌套的靶子,那么尽量通过
ID 来援用。

假设远程服务重临的数量是如此的:

[{
  id: 1,
  title: 'Some Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}, {
  id: 2,
  title: 'Other Article',
  author: {
    id: 1,
    name: 'Dan'
  }
}]

那么,转变来以下方式会更有效用:

{
  result: [1, 2],
  entities: {
    articles: {
      1: {
        id: 1,
        title: 'Some Article',
        author: 1
      },
      2: {
        id: 2,
        title: 'Other Article',
        author: 1
      }
    },
    users: {
      1: {
        id: 1,
        name: 'Dan'
      }
    }
  }
}

范式化的存款和储蓄令你的数据的一致性越来越好,上例中,即使更新了users[1].name,那么在展示
articles 的 component 中,小编姓名也被更新了。

实际守旧关全面据库的统一盘算条件就是这样,只但是随着对数据布满工夫和品位扩大性的渴求(丢掉了必然水平的多寡一致性),服务端数据的冗余越来越多。然而回去客户端,由于要求保留的数量总数相当的小(往往就是用户近日访问数据的缓存),也不曾分布式的渴求,由此范式化的数额存款和储蓄就更有优势了。除了能够博得一致性,还足以减去存款和储蓄空间(存款和储蓄空间在客户端特别难得)。

除开,范式化的囤积也便于前边讲到的 Reducer 局地化,便于将大的
Reducer 分割为一雨后春笋小的 Reducers

由于服务器端重临的 JSON
数据(以往常见的方法)往往是冗余而非范式的,因而,大概供给一些工具来援助您转移,譬如:https://github.com/gaearon/normalizr
, 尽管相当多时候本身决定会更使得一些。

Redux 中的函数字传送递及原理

当调用了装有操作全局 state 的函数时,将经过一层层的函数字传送递及调用,如

图片 1

问:为啥不直接运用 reducer(currentState, {type:’ADD_FILM’, name:
‘Minions’})) 呢?

答:那样做除了在代码协会和扩张维护上提供了平价,同期也暗含了函数式编制程序的成都百货上千优点。

Reducer

上边大家以熟稔 todoApp 来看一下 Reducer 的劳作格局:

function todoAppReducer(state = initialState, action) {
  switch (action.type) {
  case SET_VISIBILITY_FILTER:
    return Object.assign({}, state, {
      visibilityFilter: action.filter
    });
  case ADD_TODO:
    return Object.assign({}, state, {
      todos: [...state.todos, {
        text: action.text,
        completed: false
      }]
    }); 
  default:
    return state;
  }
}

本条事例演示了 Reducers 是如何根据传入的 action.type 分别更新不一致的
State 字段。

要是当应用程序中存在非常多 action.type 的时候,通过贰个 Reducer 和巨型
switch 鲜明会发出难以维护的代码。此时,相比好的不二诀窍就是通过整合小的
Reducer 来产生大的 Reducer,而种种小 Reducer 只负担管理 State
的一有些字段。如下例:

import { combineReducers } from 'redux';

const todoAppReducer = combineReducers({
  visibilityFilter: visibilityFilterReducer
  todos: todosReducer
});

visibilityFilterReducertodosReducer 是多个小
Reducers,在那之中一个之类:

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

visibilityFilterReducer 仅仅担任管理 State.visibilityFilter
字段的情形(通过 action.typeSET_VISIBILITY_FILTER 的 action
来改变)。Reducers 划分是透过向 combineReducers
传递如下方式的参数完成的:

{
  field1: reducerForField1,
  field2: reducerForField2
}

filed1filed2 表示 State 中的字段,reducerForField1
reducerForField2 是对应的 Reducers,每个 Reducers 将仅仅获得
State.field1 或者 state.field2 的值,而看不到 State
下的其余字段的内容。响应的回来结果也会被统一到对应的 State
字段中。每个 Reducer 借使遇上本人无法管理的
action,那么必须原样再次回到传入的 state,或者该 Reducer
设定的启幕状态(若是传入的 stateundefined)。

使用 combineReducers 的前提是,每二个被整合的 Reducer 仅仅和 State
的一有的数据相关,举个例子:todos Reducer 只消费 State.todos
数据,也只发生 State.todos 数据。这些基本的尺码和地方提到的“State
结构设计”范式相结合,能够满意大家超过58%须要。

不过,有的时候我们正是急需在二个 Reducer 之中采访其他一个 Reducer 负责的
state,那亟需我们创造更上一层的 Reducer(Root Reducer)
来调节这一个历程,譬如:

function a(state, action) { }
function b(state, action, a) { } // depends on a's state

function something(state = {}, action) {
  let a = a(state.a, action);
  let b = b(state.b, action, a); // note: b depends on a for computation
  return { a, b };
}

在那几个例子中,大家有多个 Reducers, ab,其中,b 在企图本身的
state 的还需求依附 a 的测算结果。因此,大家就不可能依据
combineReducers 来实现这种须求,而是必要和谐写 Root Reducer
了。reduce-reducers
也能够帮大家完毕邻近的职责:

var reducers =  reduceReducers(
  combineReducers({
    router: routerReducer,
    customers,
    stats,
    dates,
    filters,
    ui
  }),
  // cross-cutting concerns because here `state` is the whole state tree
  (state, action) => {
    switch (action.type) {
      case 'SOME_ACTION':
        const customers = state.customers;
        const filters = state.filters;
        // ... do stuff
    }
  }
);

上边的事例里,在 combineReducers 的底子上,若是某个 action
须要接触跨 Reducers
的情形退换,则足以用地点的写法。reduce-reducers
组合(各个参数正是二个 Reducer)的每贰个 Reducer 都可以获得整个
State,所以请不要滥用(请参见相关斟酌:https://github.com/reactjs/redux/issues/749
),在大部状态下,假设严苛依据数据范式,通过测算的方法获得当先
Reducers
的情事是引入的点子(http://redux.js.org/docs/recipes/ComputingDerivedData.html
)。


一个 Reducer 能够拍卖二种 action.type,而 一种 action.type
也可能被四个 Reducers 管理,那是多对多的涉及。以下 Helper 函数可以简化
Reducer 的创始进程:

function createReducer(initialState, handlers) {
  return function reducer(state = initialState, action) {
    if (handlers.hasOwnProperty(action.type)) {
      return handlers[action.type](state, action);
    } else {
      return state;
    }
  }
}

export const todosReducer = createReducer([], {
  [ActionTypes.ADD_TODO](state, action) {
    let text = action.text.trim();
    return [...state, text];
  }
}

React-Redux

Redux 并不信赖于 React,它帮衬多样框架 Ember、Angular、jQuery 以至纯
JavaScript。但骨子里,它更适于由 数据更新 UI 的框架。如 React、Deku。
地点的章节最终通过 bindActionCreators 获得全体操作全局 state
的函数群集,在与 React
搭配时,就能够将那一个函数分发到各种对应的机件中,进而组件具有了操作全局的
state 的功效。在上节中能够得到,调用操作全局 state 的函数,最后将更新
state。当 redux 与 react 组合,在更新 state 时,将会触发 重新渲染
组件的函数,进而组件获得更新。
react-redux 主要提供八个零部件来贯彻上述成效。

Store

在 Redux 中,Store 对象正是用来保障应用程序状态的对象。构造 Store
对象,仅须求提供二个 Reducer 函数就可以。如前所述,这一个 Reducer
函数是担负解释 Action
对象的语义,进而改换其中间情形(也正是应用程序的情状)。

故此 Store 对象有多少个十分重要方法,一个次要方法:

  1. store.getState(): 获取近期的里边景观对象。
  2. store.dispatch(action): 将一个 action 对象发送给 reducer

贰个附带方法为:const unsure = store.subscribe(listener),用来订阅状态的生成。在
React + Redux 的次第中,并不推荐使用 store.subscribe
。可是倘令你的应用程序是依照 Observable
情势的,则足以用这一个点子来展开适配;举例,你能够通过这么些措施将 Redux
和您的 FRP (Functional Reactive Programming) 应用结合。

下边那么些事例演示了 Store 是什么树立的:

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

const todoAppReducer = combineReducers(reducers);
const store = createStore(todoAppReducer);  // Line 5

store.dispatch({type: 'ADD_TODO', text: 'Build Redux app'});

我们也得以在 createStore 的时候为 Store 钦命四个始发状态,例如替换第
5 行为:

const store = createStore(reducers, window.STATE_FROM_SERVER);

本条例子中,早先状态来自于保存在浏览器 window 对象的
STATE_FROM_SERVER 属性。那脾性格可不是浏览器内置属性,是我们的 Web
Server 在回来的页面文件中以内联 JavaScript 格局放置的。那是一种
Universal(Isomorphic) Application 的落到实处格局。Client 无需发起第叁个 AJAX
API 伏乞,就足以一向从当下页面中向来获得初叶状态。

Connect

Connect 组件首要为 React 组件提供 store 中的部分 state 数据 及 dispatch
方法,那样 React 组件就能够因而 dispatch 来更新全局 state。在 React
组件中,假诺你希望让组件通过调用函数来更新 state,可以经过应用 const
actions = bindActionCreators(FilmActions, dispatch); 将 actions 和
dispatch 揉在一道,成为具有操作 store.state 的 actions。最终将 actions
和 state(state.films)以 props 格局传入子组件中。如

import { connect } from 'react-redux';
import * as flimActions from '../actions/films';
// 其他模块引入..

class FilmApp extends Component {
  render() {
// 从 react-redux 注入
const { films, dispatch } = this.props;

  // 生成具有操作 state 能力的 actions
  const actions = bindActionCreators(flimActions, dispatch);

  // 为各个 React 组件提供 state 数据 及 actions
  return (
<div>
  <Header films={films} actions={actions}/>
  <Section films={films} deleteFilm={actions.deleteFilm}/>
</div>
  );
  }
}

// state 将由 store 提供
function select(state) {
  return {
films: state.films
  };
}

// 最终暴露 经 connect 处理后的组件
export default connect(select)(FilmApp);

由上,在 redux 提供的 connect 函数中,select 函数用于筛选 state
的有的数据,最后和 dispatch 以 props 的格局传给 React
组件(FilmApp)。FilmApp 就可通过 this.props 来博取 store 中的 state 及
dispatch。

在 redux 中,未有与 redux 有一贯关联的组件称为木偶组件,如 FilmApp
下的子组件,不理外面纷纭扰扰,只略知一二本身独具了 state 及 具有操作 state
数据的 actions 方法。

当木偶组件使用 actions 方法,更新了 store.state 的多少时,将会触发 store
中的 subscribe 所注册的函数。而其间一个报了名函数,就在 Connect
组件中沉默不语注册了。

// 在 Connect 中
this.store.subscribe(this.handleChange.bind(this));

即当 actions 改动了 state 时,会调用注册函数 handleChange。从而实行“阿米诺骨牌式” 的函数施行有关反应。更新了 state,并选用新的数目重复
render 组件。实际上是为智能组件 FilmApp(传入 connect 的零件)传入新的
props,因为各种子成分是透过援引父级组件的
props,所以将拓展一流超级的出入数据更新,最终效果正是页面更新了。
实质上,这里与简便的公布订阅格局类似。使用 store.subscribe(cb);
来订阅三个回调函数,子组件实行 action 操作 store.state
时张开揭露,实践了回调函数。
在 react-redux 中,数据的流向及相应的反应,如

图片 2

Action

在 Redux 中,改变 State 只可以透过 action。并且,每一个 action
都必须是 Javascript Plain Object,举个例子:

{
  type: 'ADD_TODO',
  text: 'Build Redux app'
}

Redux 要求 action
是可以被种类化的,使那得应用程序的情事保存、重播、Undo
之类的效果与利益能够被完毕。由此,action
中不可能包括诸如函数调用那样的不可连串化字段。

action 的格式是有建议规范的,能够分包以下字段:

{
  type: 'ADD_TODO',
  payload: {
    text: 'Do something.'  
  },
  `meta: {}`
}

如果 action 用来表示出错的情事,则恐怕为:

{
  type: 'ADD_TODO',
  payload: new Error(),
  error: true
}

type 是供给求有的属性,别的都是可选的。完整提议请参谋 Flux Standard
Action(FSA)

定义。已经有无数第三方模块是基于 FSA 的预定来开采了。

Provider

Connect 组件须求 store。这些供给由 Redux 提供的另八个组件 Provider
来提供。源码中,Provider 承接了 React.Component,所以能够以 React
组件的款式来为 Provider 注入 store,进而使得其子组件能够在上下文中收获
store 对象。如

<Provider store={store}>
{() => <FilmApp /> }
</Provider>

在 React 0.13 及从前的版本中,Provider 渲染子组件是透超过实际行children(),如

Action Creator

事实上,创建 action
对象非常少用这种每一遍直接表明对象的方法,更多地是因此一个开立函数。那么些函数被叫做Action Creator,例如:

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

Action Creator 看起来很轻松,可是一旦结合上 Middleware
就能够变得特别灵活。

更多

编纂状态的实时预览 redux-dev-tools
https://github.com/gaearon/redux-devtools
恢宏的连锁参照他事他说加以考察 awesome-redux
https://github.com/xgrommx/awesome-redux
转载自AlloyTeam:http://www.alloyteam.com/2015/09/react-redux/

Middleware

若果您用过
Express,那么就能理解它的
Middleware 系统。在 HTTP Request 到 Response 处理进度中,一多级的
Express Middlewares 起着分裂的职能,有的 Middleware 担任记录
Log,有的担负更动内部特别为特定的 HTTP Status 再次来到值,有的担当将 Query
String 调换到 request 对象的特定属性。

Redux Middleware 的布置性理念确实是出自于 Express 。其利害攸关机制为,创设三个
store.dispatch 的链条,各种 middleware 是链条中的三个环节,传入的
action 对象日益管理,直到最后吐出来是 Javascript Plain
Object。先来看八个例证:

import { createStore, combineReducers, applyMiddleware } from 'redux';

// applyMiddleware takes createStore() and returns// a function with a compatible API.
let createStoreWithMiddleware = applyMiddleware(
  logger,
  crashReporter
)(createStore);

// Use it like you would use createStore()let todoApp = combineReducers(reducers);
let store = createStoreWithMiddleware(todoApp);

以此事例中,loggercrashReporter 那七个 Middlewares 分别落成记录
action 日志和记录 action 管理特别的效果与利益。

logger 的代码如下:

// Logs all actions and states after they are dispatched.
const logger = { getState } => next => action => {
  console.log('dispatching', action);
  let result = next(action);
  console.log('next state', getState());
  return result;
};

logger 是一个
currying
(那是函数式编程的贰个基本概念,对比 Flux,Redux
大批量使用了函数式编制程序的范式)之后的函数。next 则是下二个 Middleware
再次回到的 dispatch 函数(后边会有剖判)。对于二个 Middleware 来讲,有了
store对象,就可以透过 store.getState()
来获取如今的运用状态以供决策,有了 next ,则足以决定传递的流水生产线。

ES6 的 Fat Arrow Function
语法(logger = store => next => action =>)让原本 function 返回
function 的语法变得更简明(I love ☕️script!)。

工业化的 logger
达成能够敬仰:https://github.com/fcomb/redux-logger

https://github.com/fcomb/redux-diff-logger
。同一个小编写了四个,前边这一个支撑 State 的反差展现。

vanilla promise

Middleware 还足以用来对传播的 action 实行调换,上面这几个事例里,传入的
action 是三个 Promise(分明不吻合 action 必须是 Javascript Plain
Object 的须求),由此要求开始展览转变:

/**
 * Lets you dispatch promises in addition to actions.
 * If the promise is resolved, its result will be dispatched as an action.
 * The promise is returned from `dispatch` so the caller may handle rejection.
 */
const vanillaPromise = { getState, dispatch } => next => action => {
  if (typeof action.then !== 'function') {
    return next(action);
  }
  // the action is a promise, we should resolve it first
  return Promise.resolve(action).then(dispatch);
};

那一个例子中,假诺传入的 action 是一个 Promise(即满含 .then
函数,那只是一个简练的判断),那么就实行那些 Promise,当 Promise
实施成功后,将结果直接传送给 store.dispatch(那么些例子中大家短路了
Middlewares 链中的后续环节)。当然,大家要有限支撑 Promise
的推行结果回到的是 Javascript Plain Object。

这种用法大概并非常用,不过从这一个例子咱们得以体会到,大家能够定义自身
action 的语义,然后经过相应的 middleware
实行深入分析,爆发一定的施行逻辑以转换最后的 action
对象。那么些试行进程只怕是一路的,也说不定是异步的。

从那一个事例你恐怕也会意识,假如们也装载了 logger Middleware,那么
logger 能够知道 Promise action 进入了 dispatch
函数链条,可是却未曾机遇掌握最终 Promise
施行成功/失利后发生的职业,因为无论 Promise
实施成功与否,都会一向调用最原始的 store.dispatch,没有走 Middlewares
创建的 dispatch 函数链子。

对 Promise
的完全协理请参见:https://github.com/acdlite/redux-promise

Scheduled Dispatch

上边这一个例子略微复杂一些,演示了如何推迟推行三个 action
dispatch

/**
 * Schedules actions with { meta: { delay: N } } to be delayed by N milliseconds.
 * Makes `dispatch` return a function to cancel the interval in this case.
 */
const timeoutScheduler = store => next => action => {
  if (!action.meta || !action.meta.delay) {
    return next(action);
  }

  let intervalId = setTimeout(
    () => next(action),
    action.meta.delay
  );

  return function cancel() {
    clearInterval(intervalId);
  };
};

以此事例中,timeoutScheduler Middleware 假如发掘传入的 action
参数带有 meta.delay 字段,那么就感觉那个 action
须要延时发送。当证明的延迟时间(meta.delay)到了,action
对象才会被送往下多个 Middleware 的 dispatch 方法。

上面那些 Middleware 极其轻易,可是却提供了特别灵活的用法。

Thunk

即便不打听 Thunk 的概念,能够先阅读
http://www.ruanyifeng.com/blog/2015/05/thunk.html

thunk Middleware 的兑现特别轻松:

const thunk = store => next => action =>
  typeof action === 'function' ?
    action(store.dispatch, store.getState) :
    next(action);

上边包车型客车事例装载了 thunk,且 dispatch 了一个 Thunk 函数作为 action

const createStoreWithMiddleware = applyMiddleware(
  logger,
  thunk
  timeoutScheduler
)(createStore);
const store = createStoreWithMiddleware(combineReducers(reducers));

function addFave(tweetId) {
  return (dispatch, getState) => {
    if (getState.tweets[tweetId] && getState.tweets[tweetId].faved)
        return;

    dispatch({type: IS_LOADING});
    // Yay, that could be sync or async dispatching
    remote.addFave(tweetId).then(
      (res) => { dispatch({type: ADD_FAVE_SUCCEED}) },
      (err) => { dispatch({type: ADD_FAVE_FAILED, err: err}) },
  };
}

store.dispatch(addFave());

本条例子演示了 “收藏” 一条天涯论坛的相关的 action
对象的发出进程。addFave 作为 Action Creator,再次来到的不是 Javascript
Plain Object,而是贰个接收 dispatchgetState 作为参数的 Thunk
函数。

thunk Middleware 开掘传入的 action 是那样的 Thunk
函数时,就可感觉该函数配齐 dispatchgetState 参数,让 Thunk
函数得以推行,不然,就调用 next(action) 让后续 Middleware 获得
dispatch 的机会。

在 Thunk 函数中,首先会判断当前利用的 state 中的搜狐是或不是曾经被 fave
过了,若无,才会调用远程方法。

假如急需调用远程方法的话,那么首首发出 IS_LOADING action,告诉
关怀这一个境况的reducer 三个长距离调用运维了。进而让 reducer
能够革新对应的 state 属性。那样关怀备至此情景的 UI Component
则能够据此更新分界面提示音讯。

长距离方法若是调用成功,就能够 dispatch 代表成功的 action
对象({type: ADD_FAVE_SUCCEED}),不然,发生的便是意味着败北的 action
对象({type: ADD_FAVE_FAILED, err: err}),自然会有关怀那七个 action
reducer 来据此更新意况。无论如何,reducer 最后接受的 action
对象自然是这种 Javascript Plain Object。

当 Thunk Middleware 处理了 Thunk 函数类型的 action
之后,若是有布置了另外后续 Middlewares, 则将被跳过去而并未有时机实行。

例如:我们的 Middlewares 配置为
applyMiddleware(logger, thunk, timeoutScheduler),当 action 是 Thunk
函数时,这个 action 将从未机遇被 timeoutScheduler Middleware
执行,而 logger Middleware 则有机缘在 thunk Middleware
此前实行。种种 Middleware 本人决定给不给后续 Middleware 管理的机会。

applyMiddleware

拼装 Middlewares 的工具函数是 applyMiddleware,该函数的效仿达成如下:

function applyMiddleware(store, middlewares) {
  middlewares = middlewares.slice();
  middlewares.reverse();

  let next = store.dispatch;
  middlewares.forEach(middleware =>
    next = middleware(store)(next)
  );

  return Object.assign({}, store, { dispatch: next });
}

结合 Middleware 的写法:

const logger = store => next => action => {
  console.log('dispatching', action);
  let result = next(action);
  console.log('next state', store.getState());
  return result;
};

咱俩得以看到,给 Middleware 传入 storenext
之后,重返的是三个新的 dispatch 方法。而传播的 next 参数则是事先
Middleware 再次回到的 dispatch 函数。那样,在真的传入 action
在此以前,大家收获了二个串联在协同的 dispatch
函数,该函数用来替代原先的store.dispatch 方法(通过
Object.assign(...))。Redux Middleware 机制的目标,便是以插件情势更改
store.dispatch 的作为艺术,进而能够管理分歧类型的 action
输入,获得终极的 Javascript Plain Object 情势的 action 对象。

每二个 Middleware 能够获取:

  1. 最初的 store 对象 (dispatch 属性依旧本来的),因而,能够通过
    store.getState 获得近日的情形,以及经过原来的 dispatch
    对象直接宣布 action 对象,跳过任何 Middleware dispatch
    方法(next)。上面 vanillaPromise 演示了这么的用法。
  2. next 方法: 前一个Middleware 返回的 dispatch 方法。当前
    Middleware 能够依照本身对 action 的推断和管理结果,决定是不是调用
    next 方法,以及传入什么样的参数。


newStore = applyMiddleware(logger,thunk,timeoutScheduler)(store))
那样的表明为例,timeoutScheduler 得到的next 参数便是固有的
store.dispatch 方法;thunk 拥有 timeoutScheduler 返回的 dispatch
方法,而 logger 又拥有 thunk 返回的 dispatch 方法。最后新生成的
newStoredispatch 方法规是 logger 再次回到的。因而实际的 action
流动的一一先到 logger 返回的 dispatch 方法,再到 thunk 返回的
dispatch 方法,最后到 timeoutScheduler 返回的 dispatch 方法。

需求注意一点, logger 因为排在 dispatch
链条的率先个,由此能够赢得步入的每多少个 action 对象。不过出于别的Middleware 有希望异步调用 dispatch (异步调用前三个 Middleware 再次来到的
dispatch 方法大概原有的 store.dispatch ),因此,logger
并自然有空子领会 action 最后是怎么传递的。

Middleware 能够有那几个游戏的方法的,上边文书档案列出了 Middleware
的原理和多样Middlewares:http://rackt.github.io/redux/docs/advanced/Middleware.html

store/reducer 是 Redux 的最中心逻辑,而 Middleware
是其外围的一种扩张情势,仅肩负 action 对象的产生。可是由于 Redux
对于主题部分的限量极度严苛(保持核心概念的简短):例如,reducer
必须是手拉手的,实际工程需要所带来的须要都被推到了 Dispatch/Middleware
那有的,官方文书档案提到的接纳方式则起到了”最棒实施”的教导意义。

Higher-Order Store

Middleware 是对 store.dispatch 方法的强大机制。但稍事时候则须求对全部
store 对象都进行扩张,那就引进了 Higher-Order Store 的定义。

其一定义和 React 的 Higher-Order
Component

概念是附近的。https://github.com/gaearon/redux/blob/cdaa3e81ffdf49e25ce39eeed37affc8f0c590f7/docs/higher-order-stores.md
,既提供八个函数,接受 store 对象作为输入参数,产生一个新的 store
对象作为重回值。

createStore => createStore'

Redux 建议我们在 Middleware 无法满意扩张须求的前提下再利用 Higher-Order
Store,与 Redux 配套的
redux-devtools
正是七个事例。

Binding To React (React-Native)

地点的章节介绍了 Redux 的主题组组件和数码流程,能够由此下图回味一下:

                                                                                      ┌──────────────┐
                        ┌─────────────┐                                           ┌──▶│ subReducer 1 │
                   ┌───▶│Middleware 1 │                                           │   └──────────────┘
                   │    └─────────────┘                                           │           │       
                   │           │                                                  │           ▼       
┌─────────────┐    │           │              ┌───────────────┐    ┌──────────┐   │   ┌──────────────┐
│   action'   │────┘           ▼          ┌──▶│store.dispatch │───▶│ reducer  │───┘   │ subReducer m │
└─────────────┘         ┌─────────────┐   │   └───────────────┘    └──────────┘       └──────────────┘
                        │Middleware n │   │                                                   │       
                        └─────────────┘   │                                                   │       
                               │          │                                                   ▼       
                               │          │                                           ┌──────────────┐
                               └──────────┘                                           │    state     │
                               plain action                                           └──────────────┘                                                            

Redux
化解的是应用程序状态存款和储蓄以及哪些转移的主题素材,至于怎么用,则借助于另外模块。关于怎样在
React 也许 React-Native 中采纳 Redux
,则须要仿照效法react-redux

react-redux
是 React Components 怎么样利用 Redux 的
Binding。下边大家来深入分析一个切实可行的事例。

import { Component } from 'react';

export default class Counter extends Component {
  render() {
    return (
      <button onClick={this.props.onIncrement}>
        {this.props.value}
      </button>
    );
  }
}

这是二个 React Component,显示了一个开关。按下这么些按键,就能够调用
this.props.onIncrementonIncrement的具体内容在底下的例证中,
起成效为每趟调用 onIncrement 就会 dispatch {type: INCREMENT}
Action 对象来更新 Store/State


react-redux
中,那样的 Component 被称为 “Dumb” Component,既其自个儿对 Redux
完全无知,它只知道从 this.props 获取供给的 Action Creator
况兼询问其语义,适当的时候调用该办法。而 “Dumb” Component
须要表现的外界数据也来源于于 this.props

如何为 “Dumb” Component 准备 this.props
呢?react-redux
提供的 connect 函数支持你完了这一个功用:

import { Component } from 'react';
import { connect } from 'react-redux';

import Counter from '../components/Counter';
import { increment } from '../actionsCreators';

// Which part of the Redux global state does our component want to receive as props?
function mapStateToProps(state) {
  return {
    value: state.counter
  };
}

// Which action creators does it want to receive by props?
function mapDispatchToProps(dispatch) {
  return {
    onIncrement: () => dispatch(increment())
  };
}

export default connect(   // Line 20
  mapStateToProps,
  mapDispatchToProps
)(Counter);

第 20 行的 connectstate 的某部(些)属性映射到了 Counter
Component 的 this.props 属性中,同不经常间也把针对特定的Action Creator
dispatch 方法传递给了 this.props。这样在 Counter Component
中只有经过 this.props 就足以产生 action dispatching 和
应用程序状态获得的动作。

借使 connect
函数省掉第1个参数,connect(mapStateToProps)(Counter),那么 dispatch
方法会被平昔传送给 this.props。这不是推荐的办法,因为那意味
Counter 要求领悟 dispatch 的机能和语义了。

Components 的嵌套

您可以在您的机件树的别的二个档期的顺序调用 connect 来为下层组件绑定状态和
dispatch 方法。不过仅在您的顶层组件调用 connect
实行绑定是首荐的法门。

Provider Component

地点的事例实际上是不行施行的,因为 connect 函数实际并从未 Redux
store 对象在哪个地方。所以我们必要有叁个体制让 connect 知道从您这里获取
store 对象,那是通过 Provider Component
来设定的,Provider Component 也是
react-redux
提供的工具组件。

React.render(
  <Provider store={store}>
    {() => <MyRootComponent />}
  </Provider>,
  rootEl
);

Provider Component 应该是您的 React Components 树的根组件。由于 React
0.13 版本的主题素材,Provider Component
的子组件必须是三个函数,这些主题素材将要 React 0.14 中期维修复。

Provider Component 和 connect 函数的相称,使得 React Component 在对
Redux 完全无感的意况下,仅透过 React
本人的建制来赢得和保险应用程序的事态。

selector

在地方的例证中,connect(mapStateToProps,mapDispatchToProps)(Counter)
中的 mapStateToProps 函数通过再次来到叁个炫人眼目对象,钦赐了什么样
Store/State 属性被映射到 React Component 的
this.props,那几个形式被称作 selectorselector 的效应正是为 React
Components 构造适合自个儿须要的图景视图。selector 的引入,降低了 React
Component 对 Store/State 数据结构的依赖性,利于代码解耦;同不经常间由于
selector
的落实完全部都以自定义函数,因而也许有丰富的八面驶风(比如对原有状态数据开始展览过滤、汇总等)。

reselect
那个类型提供了带 cache 成效的 selector。如果 Store/State 和结构 view
的参数未有变化,那么每趟 Component
获取的多寡都未来自于上次调用/总结的结果。得益于 Store/State Immutable
的庐山真面目,状态变化的检验是极其迅猛的。

总结

  1. Redux 和 React 未有平素关联,它瞄准的靶子是行使状态管理。
  2. 主干概念是 Map/Reduce 中的 Reduce。且 Reducer 的进行是共同,发生的
    State 是 Immutable 的。
  3. 改变 State 只好通过向 Reducer dispatch actions 来成功。
  4. State 的例外字段,能够经过分化的 Reducers
    来分别维护。combineReducers 担任组合这一个 Reducers,前提是每种
    Reducer 只可以有限支撑团结关切的字段。
  5. Action 对象只可以是 Javascript Plain Object,然则经过在 store
    上装载 middleware,则足以自便定义 action
    对象的情势,反正会有特定的 middleware 负担将此 action 对象产生Javascript Plain Object。能够以middleware
    链条为凑集式点心完成无数决定逻辑,比方 Log,Undo, ErrorHandler 等。
  6. Redux 仅仅专注于接纳状态的珍视,reducerdispatch/middleware
    是多少个常用扩大点、Higher-order Store 则仅针对急需扩充全体 Store
    作用时使用。
  7. react-redux 是 Redux 针对 React/React-Native 的
    Binding,connect/selector 是扩充点,担当将 store 中的状态增多到
    React componentprops 中。
  8. Redux
    借用了相当多函数式编程的构思,了然函数式编制程序会利于明白其落到实处原理,即便使用它无需通晓相当多函数式编制程序的定义。和
    Flux 相比较,Redux
    的定义更简短、约定更严格、状态更分明、而是增添却越来越灵敏。
  9. 通过
    https://github.com/xgrommx/awesome-redux
    能够赢得大量参阅。

另外参照他事他说加以考察

大而全的具备 Redux 参照他事他说加以考察资料。

https://github.com/xgrommx/awesome-redux

Slack 讨论组

加入
https://reactiflux.slack.com
Team,然后选用 redux channel。