参考链接,最近静下心细读源码

作者: 前端  发布:2019-11-20

Redux 源码深入分析及应用

2018/05/23 · JavaScript · Redux

原作出处: 网易技巧团队   

选取redux+react本来就有风度翩翩段时间,刚早先使用没有深远摸底其源码,近来静下心细读源码,感触颇深~

本文主要蕴含Redux设计观念、源码深入分析、Redux应用实例应用八个方面。

参照链接:

1.Redux 安顿意见

  Web 应用是二个状态机,视图与气象是种种对应的

  全体的景色,保存在叁个目的里面

 

Redux 是如今提出的 Flux 理念的后生可畏种解决方案,在它前边也可以有 reflux 、 fluxxor 等高素质的文章,但短暂多少个月就在 GitHub 上获近万 star 的大成让这一个后来者居上逐步变为 Flux 的主流推行方案。

背景:

React 组件 componentDidMount 的时候初阶化 Model,并监听 Model 的 change 事件,当 Model 发生更动时调用 React 组件的 setState 方法重新 render 整个组件,最终在组件 componentWillUnmount 的时候撤废监听并销毁 Model。

最先先兑现三个轻便实例:比如add加法操作,只供给经过React中 setState 去调整变量增加的场馆,特轻便方便。

只是当大家须求在类型中扩大乘法/除法/幂等等复杂操作时,就供给规划多少个state来调控views的更换,当项目变大,里面包涵状态过多时,代码就变得难以保险並且state的改变不可预测。大概需求追加三个小效能时,就能够孳生多处改变,招致支出频率下跌,代码可读性不高

举例现在利用很多backbone方式:图片 1

如上图所示,能够看看 Model 和 View 之间涉及错综复杂,前期代码难以维护。

为了消除上述难题,在 React 中引进了 Redux。Redux 是 JavaScript 事态容器,提供可预测化的情状管理方案。上边详细介绍~~

  • Redux国语文书档案
  • Redux 入门教程-阮一峰
  • 看漫画,学 Redux
  • 在react-native中使用redux
  • [React Native]Redux的宗旨使用办法
  • Redux管理复杂应用数据逻辑

2.基本概念和API

  Redux 的着力便是 store, action, reducer   store.dispatch(action) ——> reducer(state, action) ——> final state

(1卡塔尔国store 就是保存数据的地点,redux 提供createStore 函数,生成Store

    store = redux.createStore(reducer, []);

        store.getState() //重返store的近日场馆

  Store 允许采纳store.subscribe方法设置监听函数,意气风发旦 State 爆发变化,就自动实行这一个函数。

  store.subscribe(listener);

  store.subscribe 方法重返三个函数,调用这一个函数就足以扫除监听

  let unsubscribe = store.subscribe(() =>

    console.log(store.getState())

  );

  unsubscribe(); //衰亡监听

  Store 的实现

store.getState() //获取当前状态

store.dispatch() //触发action

store.subscribe() //监听state状态

import { createStore } from ‘redux’;

let { subscribe, dispatch, getState } = createStore(reducer, window.STATE_FORM_SERVER);

window.STATE_FORM_SERVER //是整个应用的初始状态值

 (2卡塔尔action 是多少个平时的object,必得有叁个type属性,评释行为的类型。

  const action = {

    type: ’add_todo’,

    text: ‘read’,

    time

    …

  }

  action描述当前发出的政工,改变State的天下无双办法就是因此action,会将数据送到store。

  平常用actionCreator 工厂格局产生,View要产生的音信类型对应action的门类,手写起来很棘手。

  const ADD_TODO = “添加 todo”;

  function addTodo(text){

    return {

           type: ADD_TODO,

      text 

          }

  }

  const action = addTodo(‘Learn’);

  addTodo 方法正是一个Action Creator

  View 发出Action的必须要经过的路路线 store.dispatch(action) //触发事件 

 (3卡塔尔国reducer 其实正是五个家常函数,首要用于退换state. Store 收到View 发出的Action 未来,必得回到贰个新的State,View 才会产生变化。 而这一个计算新的State的进度就叫Reducer.

  const reducer = function(state, action){

  switch(state.text){

             case ‘add_todo’:

  return state.contact(‘…’);

              default:

  return state;

         }

  }

   当然实际费用不像上边例子这么轻巧,供给在创造state的时候就清楚state的乘除准则,将reducer传入:

  store = redux.createStore(reducer);

  Reducer 纯函数,只要有相像的输入必然重返同样的输出。不能校订原先的state而是通过Reducer再次回到二个新的state。

//state 是一个对象

function reducer(state, action){

return Object.assign({},state, {thingToChange});

         return {…state, …newState};

}

//state 是一个数组

function reducer(state, action){

return […state, newItem];

}

正如 Redux 合法所称,React 防止在视图层直接操作 DOM 和异步行为 ( removing both asynchrony and direct DOM manipulation 卡塔 尔(阿拉伯语:قطر‎,来拆开异步和生成那生机勃勃对相恋的人。但它还是把意况的治本交到了大家手中。Redux 便是大家的图景管理小管家。

目的:

1、深刻理解Redux的兼顾观念

2、深入分析Redux源码,并结合实际应用对源码有越来越深档期的顺序的敞亮

3、实际工程运用中所蒙受的主题素材总结,幸免重新踩坑

目录

  • 选拔场景
  • 运用的三原则
    • 纯净数据源
    • 情景是只读的
    • 透过纯函数改进State
  • redux状态处理的流水生产线及相关概念
    • store
    • Action
    • Action 创设函数(Action Creator)
    • Reducer
  • redux如何与组件结合
    • 现实示例1
    • 具体示例2

3.Reducer的拆分和归拢

在实际项目中,reducer 很巨大,不易阅读管理,大家得以将reducer 拆分成小的函数,差异的函数对应管理不相同的习性。然后将其联合成八个大的reducer

Reducer 提供了三个主意combineReducers方法来统大器晚成reducer.

const chatReducer = (state = defaultState, action = {}) => {
  return {
    chatLog: chatLog(state.chatLog, action),
    statusMessage: statusMessage(state.statusMessage, action),
    userName: userName(state.userName, action)
  }
};

import { combineReducers } from 'redux';

const chatReducer = combineReducers({
  chatLog,
  statusMessage,
  userName
})

export default todoApp;

您能够把具备子reducers 放在多个文件夹里,然后统风流倜傥引入。

import { combineReducers } from 'redux'

import * as reducers from './reducers'

const reducer = combineReducers(reducers)

安利的话先一时半刻说起那,此次大家闲谈 React-Redux 在沪江前端团队中的施行。

黄金年代、Redux设计观念

动用处景

React设计意见之一为单向数据流,那从意气风发边有助于了数码的保管。不过React自身只是view,并未提供康健的数码管理方案。随着应用的再三复杂化,要是用react营造前端采取的话,将在应对纷繁复杂的多少通信和管理,js供给保证更加多的景观(state卡塔尔国,那么些state也许包括客户新闻、缓存数据、全局设置情形、被激活的路由、被入选的价签、是不是加载动作效果恐怕分页器等等。

那时,Flux架构应时而生,Redux是其最文雅的兑现,Redux是三个不正视任何库的框架,然而与react结合的最棒,此中react-redux等开源组件就是把react与redux组合起来进行调用开辟。

备注:

1.比如你不知道是或不是供给 Redux,那正是无需它

2.独有相逢 React 实在搞定不了的主题材料,你才须求 Redux

Redux使用景况:

  • 某些组件的景色,须要分享
  • 有些状态须求在其他地点都足以得到
  • 一个组件须要转移全局状态
  • 五个组件须求更改另二个组件的境况

比方,论坛应用中的晚上设置、回到顶上部分、userInfo全局分享等境况。redux最终目标正是让情况(state)变化变得可预测.

4.中间件和异步操作

作者们接收redux ,客商发生action,Reducer算出新的state,然后再次渲染分界面。这里Reducer是随时算出state,立时响应的,同步施行的种种。 

可是生机勃勃旦我们必要实施异步达成,Reducer实施完之后,自动执行呢? 这里就必要选择middleWare(中间件)。

中间件加在哪个地点适度?大家来轻巧深入分析下。

  1. Reducer 是纯函数,用来计量state,相近的输入必然获得平等的出口,理论上纯函数分裂意读写操作。

  2. View和state是各类对应,是state的表现,未有拍卖本领。

  3. Action 是存放数据的指标,即音信的载体,被触发操作。

末段开掘,唯有加在store.dispatch() 比较适中。增多日志成效,把 Action 和 State 打字与印刷出来,能够对store.dispatch扩充如下改换。

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log('dispatching', action);
  next(action);
  console.log('next state', store.getState());
}

小结:中间件其实正是一个函数,对store.dispatch方法实行了改变,在发出 Action 和实践 Reducer 这两步之间,增添了其它成效。

中间件的用法:

const store = createStore(
  reducer,
  initial_state,
  applyMiddleware(logger)
);

createStore方法能够承担一切应用的发端状态作为参数,将中间件(logger卡塔尔放在applyMiddleware办法之中,传入createStore措施,就做到了store.dispatch()的功效巩固。

applyMiddleware 是Redux 的原生方法,功能是将装有中间件组成一个数组,依次试行。上面是它的源码:

export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer);
    var dispatch = store.dispatch;
    var chain = [];

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    };
    chain = middlewares.map(middleware => middleware(middlewareAPI));
    dispatch = compose(...chain)(store.dispatch);

    return {...store, dispatch}
  }
}

能够看来,中间件内部(middlewareAPI卡塔尔能够获得getState和dispatch那五个章程。

异步操作的七个减轻方案,正是让 Action Creator 重返一个 Promise 对象。看一下redux-promise的源码:

export default function promiseMiddleware({ dispatch }) {
  return next => action => {
    if (!isFSA(action)) {
      return isPromise(action)
        ? action.then(dispatch)
        : next(action);
    }

    return isPromise(action.payload)
      ? action.payload.then(
          result => dispatch({ ...action, payload: result }),
          error => {
            dispatch({ ...action, payload: error, error: true });
            return Promise.reject(error);
          }
        )
      : next(action);
  };
}

从上边代码能够见见,假设 Action 本人是二个 Promise,它 resolve 今后的值应该是一个 Action 对象,会被dispatch主意送出(action.then(dispatch)卡塔 尔(阿拉伯语:قطر‎,但 reject 未来不会有别的动作;假如 Action 对象的payload品质是四个 Promise 对象,那么无论是 resolve 和 reject,dispatch情势都会时有爆发 Action。

0. 放弃

你从未看错,在最早以前我们先是谈谈一下什么动静下不该用 Redux。

所谓杀鸡焉用宰牛刀,任何技术方案都有其适用途景。作为二个考虑的实践方案,Redux 必然会为落到实处观念立规矩、铺根基,放在复杂的 React 应用里,它会是“理之当然”,而身处结构不算复杂的行使中,它只会是“长篇大论”。

假使我们就要构建的施用没有必要多层组件嵌套,状态变化简单,数据单生机勃勃,那么就应扬弃Redux ,接受单纯的 React 库 或任何 MV* 库。毕竟,未有人乐于雇佣二个收取金钱比本身收入还高的财务顾问。

背景:

金钱观 View 和 Model :八个 view 大概和三个 model 相关,二个 model 也恐怕和多个 view 相关,项目复杂后代码耦合度太高,难以维护。

redux 应运而生,redux 中挑郑城概念reducer,将全部复杂的 state 聚焦管理,view 层客商的操作不可能直接改造 state从而将view 和 data 解耦。redux 把古板MVC中的 controller 拆分为action和reducer

采纳的三尺度

  • 单少年老成数据源

整整应用的state,存款和储蓄在唯黄金时代一个object中,同不平日间也独有一个store用于存款和储蓄这一个object.

  • 境况是只读的

唯意气风发能校正state的艺术,便是触发action操作。action是用来陈述正在爆发的风浪的三个指标

  • 由此纯函数校订State

纯函数的难题,也是缘于于函数式编制程序思想,大家在中学时学的函数就是纯函数,对于同叁个输入,必然有同等的出口。那就确认保证了数码的可控性,这里的纯函数便是reducer

5.React+Redux

redux将具备组件分为UI组件和容器组件。

UI 组件有以下多少个特色。

  1. 只负担 UI 的显现,不带有其余职业逻辑
  2. 没有动静(即不使用this.state那个变量卡塔 尔(阿拉伯语:قطر‎
  3. 抱有数据都由参数(this.props卡塔 尔(阿拉伯语:قطر‎提供
  4. 不选拔此外 Redux 的 API

UI 组件又叫做"纯组件",即它纯函数同样,纯粹由参数决定它的值。

 

容器组件的风味恰好相反。

  1.担当管理数据和事情逻辑,不担负 UI 的变现

  2.包蕴内幕

  3.使用 Redux 的 API

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意味,正是将那三种组件连起来。

import { connect } from 'react-redux'

const VisibleTodoList = connect(

  mapStateToProps,

  mapDispatchToProps

)(TodoList)

地方代码中,connect方法选取四个参数:mapStateToProps和mapDispatchToProps。

它们定义了 UI 组件的业务逻辑。前者肩负输入逻辑,将在state映射到 UI 组件的参数(props卡塔 尔(阿拉伯语:قطر‎,后面一个担任输出逻辑,就要客户对 UI 组件的操作映射成 Action。

 

1. 思路

首先,大家回想一下 Redux 的基本思路

图片 2

redux flow

当顾客与分界面人机联作时,交互作用事件的回调函数会触发 ActionCreators ,它是一个函数,再次回到多个目的,该指标引导了客商的动作类型和校勘 Model 必得的数码,那一个指标也被我们誉为 Action 。

以 TodoList 为例,加多八个 Todo 项的 ActionCreator 函数如下所示(假使不熟练 ES6 箭头函数请移步这里):

const addTodo = text => ({
    type: 'ADD_TODO',
    text
});

在上例中,addTodo 正是 ActionCreator 函数,该函数重回的靶子就是 Action 。

其间 type 为 Redux 中约定的必填属性,它的功效稍后大家会讲到。而 text 则是实践 “加多 Todo 项“ 那些动作必须的多寡。

理所当然,区别动作所要求的多少也不尽相通,如 “删除Todo” 动作,我们就必要知道 todo 项的 id,“拉取原来就有个别Todo项” 动作,大家就必要传入叁个数组( todos 卡塔尔国。形如 text 、 id 、 todos 那类属性,大家习贯称呼其为 “ payload ” 。

今昔,大家收获了一个 “绘身绘色” 的动作。它丰硕简洁,但担当 Model 的 store 临时还不亮堂如何感知那些动作进而改换数据结构。

为了管理这么些关键难点,Reducer 巧然上台。它仍然为三个函数,并且是未有副功用的纯函数。它只选用多个参数:state 和 action ,重返三个 newState 。

是的,state 正是您在 React 中熟谙的 state,但据他们说 Redux 三原则 之大器晚成的 “单豆蔻年华数据源” 原则,Reducer 幽幽地说:“你的 state 被本人承包了。”

于是乎,单生龙活虎数据源法则实施起来,是规定用 React 的顶层容器组件( Container Components 卡塔 尔(阿拉伯语:قطر‎的 state 来囤积单生龙活虎对象树,同有的时候间提交 Redux store 来治本。

此间分别一下 state 和 Redux store:state 是实在积存数据的指标树,而 Redux store 是和煦 Reducer、state、Action 三者的调治主题。

而这么前所说,Reducer 那时候手握四个关键新闻:旧的数据结构(state卡塔 尔(阿拉伯语:قطر‎,还恐怕有改造它所急需的音信(action),然后聪明的 Reducer 算盘风流倜傥敲,就能够交付四个新的 state ,进而改过数据,响应顾客。下边照旧拿 TodoList
举个例子(面生 “...” ES6 rest/spread 语法请先看这里):

//整个 todoList 最原始的数据结构。
const initState = {
    filter: 'ALL',
    todos: []
};
//Reducer 识别动作类型为 ADD_TODO 时的处理函数
const handleAddTodo = (state, text) => {
    const todos = state.todos;
    const newState = {...state, {
        todos: [
            ...todos, {
            text,
            completed: false
        }]
    }};
    return newState;
};
//Reducer 函数
const todoList = (state = initState, action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return handleAddTodo(state, action.text);
        default:
            return state;
    }
}

当接过到一个 action 时,Reducer 从 action.type 识别出该动作是要增添 Todo 项,然后路由到对应的管理方案,接着依照 action.text 达成了拍卖,重返一个newState 。进程里面,整个应用的 state 就从 state => newState 完结了气象的改造。

这几个进度让我们很自然地联想到去银行存取钱的经验,显著大家应当告诉柜台操作员要存取钱,并非远望着银行的金库自说自话。

Reducer 为大家梳理了富有改动 state 的方式,那么 Redux store 白手兴家,从有到变都应当与 Reducer 强关联。

进而,Redux 提供了 createStore 函数,他的率先个参数便是 Reducer ,用以形容 state 的转移形式。第三个是可选参数 initialState ,以前大家知道,这么些 initialState 参数也得以传给 Reducer 函数。放在这里处做可选参数的案由是为同构应用提供便利。

//store.js
import reducer from './reducer';
import { createStore } from 'redux';
export default createStore(reducer);

createStore 函数最终回到三个指标,也正是我们所说的 store 对象。首要提供多少个办法:getState、dispatch 和 subscribe。 当中getState() 得到 state 对象树。dispatch(actionCreator) 用以实行actionCreators,建起从 action 到 store 的大桥。

唯有实现情状的改换可不算完,大家还得让视图层跟上 store 的扭转,于是 Redux 还为 store 设计了 subscribe 方法。望文生义,当 store 更新时,store.subscribe() 的回调函数会更新视图层,以达成 “订阅” 的意义。

在 React 中,有 react-redux 那样的桥接库为 Redux 的融合扫平道路。所以,大家只需为顶层容器组件外包生机勃勃层 Provider 组件、再合作 connect 函数管理从 store 改造到 view 渲染的连带进度。

import store from './store';
import {connect, Provider} from 'react-redux';
import React from 'react';
import ReactDOM from 'react-dom';
import Page from '../components/page'; //业务组件
// 把 state 映射到 Container 组件的 props 上的函数
const mapStateToProps = state => { 
    return {
        ...state
    }
}
const Container = connect(mapStateToProps)(Page); //顶层容器组件
ReactDOM.render(
    <Provider store={store}>
        <Container />
    </Provider>,
    document.getElementById("root")
);

而顶层容器组件往下的子组件只需正视 props 就能够后生可畏层层地得到 store 数据结构的多寡了。就好像那样:

图片 3

store props

时至明日,大家走了二遍完整的数据流。可是,在事实上项目中,大家面没错供给尤其复杂,与此同一时候,redux 和 react 又是持有强有力扩大性的库,接下去大家将结合以上的着入眼思路,谈谈大家在实质上付出中会遭遇的有些细节难点。

两全思想:

(1卡塔尔国Web 应用是七个状态机,视图与气象是各样对应的。

(2卡塔 尔(英语:State of Qatar)全部的状态,保存在二个指标里面。

Redux 让动用的动静变化变得可预测。假设想校勘使用的情况,就务须 dispatch 对应的 action。而不可能一向校订使用的处境,因为保存那一个情形的地点(称为 store卡塔尔只有 get方法(getState) 而没有 set方法

大器晚成旦Redux 订阅(subscribe)相应框架(譬如React)内部方法,就足以采取该行使框架保险数据流动的风度翩翩致性。

redux状态管理的流程及相关概念

图片 4

image

  • store

Store 正是保存数据的地点,保存着本程序有所的redux管理的数额,你可以把它看作二个器皿。整个应用只好有一个Store。(二个store是两个指标, reducer会改动store中的有些值)

Redux 提供createStore那个函数,用来生成 Store。

import { createStore } from 'redux';
const store = createStore(fn);

上边代码中,createStore函数选拔另多少个函数作为参数,重临新生成的 Store 对象。那个fn正是reducer纯函数,常常我们在付出中也会利用中间件,来优化架构,譬喻最常用的异步操作插件,redux-thunk,倘若相称redux-thunk来创设store的话,代码示例:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/rootReudcer';

let createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
let store = createStoreWithMiddleware(rootReducer);

redux-thunk的源码及其轻巧,如下:

// 判断action是否是函数,如果是,继续执行递归式的操作。所以在redux中的异步,只能出现在action中,而且还需要有中间件的支持。
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

同步action与异步action最大的界别是:

协同只回去贰个普通action对象。而异步操作中途会再次来到一个promise函数。当然在promise函数管理实现后也会回去二个普通action对象。thunk中间件就是判别如若回到的是函数,则不传导给reducer,直到检查评定到是普通action对象,才交由reducer处理。


Store 有以下任务:

  • 提供 getState() 方法拿到 state;
  • 提供 dispatch(action) 方法立异 state;
  • 由此 subscribe(listener) 注册监听器;
  • 透过 subscribe(listener) 再次来到的函数注销监听器。

貌似情况下,大家只要求getState()和dispatch()方法即可,即能够缓和绝超越50%难题。

笔者们能够自定义中间件

譬喻说我们自定义二个足以打字与印刷出如今的接触的action以至出发后的state变化的中间件,代码更改如下:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/rootReudcer';

let logger = store => next => action => {
    if(typeof action === 'function') {
        console.log('dispatching a function')
    }else{
        console.log('dispatching', action);
    }

    let result = next(action);
    // getState() 可以拿到store的状态, 也就是所有数据
    console.log('next state', store.getState());
    return result;
}

let middleWares = {
    logger, 
    thunk
}
// ... 扩展运算符
let createStoreWithMiddleware = applyMiddleware(...middleWares)(createStore);

let store = createStoreWithMiddleware(rootReducer);

增加补充:我们自定义的中间件,也可以有对应的开源插件,redux-logger,人家的更加厉害。

即便,app中关系到登入难点的时候,可以应用redux-persist其三方插件,这一个第三方插件来将store对象存款和储蓄到地方,以致从地点苏醒数据到store中,举例说保存登入消息,下一次张开应用能够一贯跳过登入分界面,因为大家如今的接收归于内嵌程序,不登录的话也进不来,所以并不是它。

  • Action

Action 是一个对象,描述了接触的动作,仅此而已。我们约定,action 内必得选用一个字符串类型的 type 字段来表示就要执行的动作。平日它长一下以此样子:

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

除却 type 字段外,action 对象的组织完全由你自身主宰,来看一个复杂点的:

{
    type: 'SET_SCREEN_LAST_REFRESH_TIME',
    screenId,
    lastRefreshTime,
    objectId
}

平铺直叙大家会增添三个新的模块文件来积累这个actions types,譬如大家新建一个actionTypes.js来保存:

//主页actions
export const FETCH_HOME_LIST = 'FETCH_HOME_LIST';
export const RECEIVE_HOME_LIST = 'RECEIVE_HOME_LIST';
//分类页actions
export const FETCH_CLASS_LIST = 'FETCH_CLASS_LIST';
export const RECEIVE_CLASS_LIST = 'RECEIVE_CLASS_LIST';
//分类详细页actions
export const FETCH_CLASSDITAL_LIST = 'FETCH_CLASSDITAL_LIST';
export const RECEIVE_CLASSDITAL_LIST = 'RECEIVE_CLASSDITAL_LIST';
export const RESET_CLASSDITAL_STATE = 'RESET_CLASSDITAL_STATE'; 
// 设置页actions
export const CHANGE_SET_SWITCH = 'CHANGE_SET_SWITCH';
export const CHANGE_SET_TEXT = 'CHANGE_SET_TEXT';
// 用户信息
export const USER_INFO = 'USER_INFO';

引用的时候,能够:

import * as types from './actionTypes';
  • Action 创立函数(Action Creator)

Action 成立函数 正是生成 action 的章程。“action” 和 “action 创立函数” 那多少个概念相当轻松混在一同,使用时最佳注意区分。在 Redux 中的 action 创造函数只是不难的回到多个 action。

import * as types from './actionTypes';
// 设置详情页内容文字主题
let changeText = (theme) => {
    return {
        type: types.CHANGE_SET_TEXT,
        theme
    }
}   

// 函数changeText就是一个简单的action creator。

完整的action文件(setAction.js)

import * as types from './actionTypes';

let setTitle = (value) => {
    return (dispatch, getState) => {
        dispatch(changeValue(value))
    }
}

let setText = (text) => {
    return dispatch => {
        dispatch(changeText(text))
    }
}

// 修改标题颜色主题
let changeValue = (titleTheme) => {
    return {
        type: types.CHANGE_SET_SWITCH,
        titleTheme
    }
}

// 设置详情页内容文字颜色
let changeText = (textColor) => {
    return {
        type: types.CHANGE_SET_TEXT,
        textColor
    }
}

export {
    setText,
    setTitle
};

能够见到上述setTitle、setText函数,再次回到的实际不是五个action对象,而是回到了二个函数,那几个暗许redux是迫于处理的,那就必要动用中间件管理了,redux-thunk中间件用于拍卖回来函数的函数,上面也介绍了redux-thunk的使用基本办法。

  • Reducer

Store 收到 Action 今后,必须付出一个新的 State,那样 View 才会产生变化。这种 State 的测算进程就称为 Reducer。
Reducer 是七个函数,它承担 Action 和当前 State 作为参数,重返一个新的 State。

函数签字:

(previousState, action) => newState

Reducer必得保证相对十足,永久不要在 reducer 里做这一个操作:

  • 纠正传入参数;
  • 实践有副成效的操作,如 API 供给和路由跳转;
  • 调用非纯函数,如 Date.now() 或 Math.random();

完整的Reducer方法,(setReducer.js):

import * as types from '../actions/actionTypes';

const initialState = {
    titleTheme: false,
    textColor: false
}
// 这里一个技巧是使用 ES6 参数默认值语法 来精简代码
let setReducer = (state = initialState, action) => {

    switch(action.type){
        case types.CHANGE_SET_SWITCH:
            return Object.assign({}, state, {
                titleTheme: action.titleTheme,
            })

        case types.CHANGE_SET_TEXT:
            return Object.assign({}, state, {
                textColor: action.textColor
            })

        default:
            return state;
    }
}

export default setReducer

注意:

  • 不要改换 state。 使用 Object.assign() 新建了三个别本。无法这么使用 Object.assign(state, {
    titleTheme: action.titleTheme,
    }),因为它会校正第二个参数的值。你不得不把第2个参数设置为空对象。你也足以拉开对ES7议事原案对象进行运算符的支撑, 进而使用 { ...state, ...newState } 抵达同等的目标。
  • 在 default 境况下再次来到旧的 state。蒙受未知的 action 时,必要求回来旧的 state

至于拆分Reducer

此地只是比喻了二个大概的效果与利益的reducer,如若有两样的效果与利益,须要统筹比非常多reducer方法,注意每一种reducer 只担当管理全局 state 中它担当的风度翩翩部分。每一种 reducer 的 state 参数都不如,分别对应它管理的那部分 state 数据。

譬喻说大家那一个类别的reducer文件结构:

图片 5

image.png

当中rootReducer.js正是八个根reducer文件,使用了Redux 的 combineReducers() 工具类来开展包装整合。

/**
 * rootReducer.js
 * 根reducer
 */
import { combineReducers } from 'redux';
import Home from './homeReducer';
import Class from './classReducer';
import ClassDetial from './classDetialReducer';
import setReducer from './setReducer';
import userReducer from './userReducer';

export default rootReducer = combineReducers({
    Home,
    Class,
    ClassDetial,
    setReducer,
    userReducer,
})

如此依照那几个根reducer,能够生成store,请看上文store的成立进程。

6. react+redux 的 simple 示例

原理图:

图片 6

<div id="container"></div>

<script type="text/babel">

  //工厂Action

    var addTodoAction = function(text){

      return {

        type: 'add_todo',

        text: text

      }

    };


    var todoReducer = function(state,action){

      if(typeof state === 'undefined') return [];

      switch(action.type){

        case 'add_todo':

        return state.slice(0).concat({

          text: action.text,

          completed:false

        });

        default:

        return state;

      }

    };


    var store = redux.createStore(todoReducer);


    var App = React.createClass({

      //获取初始状态

      getInitialState: function(){

        return {

          items: store.getState()

        };

      },

      componentDidMount: function(){

        var unsubscribe = store.subscribe(this.onChange);

      },

      onChange: function(){

        this.setState({

          items: store.getState()

        });

      },

      handleAdd: function(){

        var input = ReactDOM.findDOMNode(this.refs.todo);

        var value = input.value.trim();

        if(value){

          store.dispatch(addTodoAction(value));

        }

        input.value = '';

      },

      render: function(){

        return(

          <div>

          <input ref="todo" type="text" placeholder="input todo type"/>

          <button onClick ={this.handleAdd}>Add</button>

          <ul>

          {

            this.state.items.map(function(item){

              return <li>{item.text}</li>

            })

          }

          </ul>

          </div>

        );

      }

    });

    ReactDOM.render(

      <App />,

      document.getElementById('container')

    )

  </script>

 

2. 细节

Action Creator:

只好通过dispatch action来改造state,那是举世无双的法门

action日常的花样是: action = { type: ‘ … ‘, data: data } action一定是有三个type属性的对象

在dispatch任何叁个 action 时将享有订阅的监听器都履行,公告它们有state的换代图片 7

redux怎么样与组件结合

如上部分介绍了Redux 涉及的基本概念,上面介绍与组件人机联作的干活流程。

梳理一下Redux的行事流程:

图片 8

image

1.第生机勃勃,顾客发生 Action。

store.dispatch(action);

2.Store 电动调用 Reducer,并且传入四个参数:当前 State 和选取的 Action。 Reducer 会重返新的 State 。

let nextState = todoApp(previousState, action);

3.state只要有调换,Store就能够调用监听函数,组件能够感知state的改动,更新View。

let newState = store.getState();
component.setState(newState);

切实示例1:

图片 9

fsdf.gif

设置页面有个switch按键,能够全局设置标题栏的主旨。

使用目录

显著的思绪须辅以分工鲜明的文件模块,技术让大家的运用达到更佳的实施效果,同一时间,统风度翩翩的组织也方便脚手架生成模板,升高开销功能。

以下的目录结构为团体友人数十次索求和纠正而来(限于篇幅,这里只关怀 React 应用的目录。卡塔 尔(英语:State of Qatar):

appPage
├── components
│   └── wrapper
│       ├── component-a
│       │   ├── images
│       │   ├── index.js
│       │   └── index.scss
│       ├── component-a-a
│       ├── component-a-b
│       ├── component-b
│       └── component-b-a
├── react
│   ├── reducer
│   │   ├── index.js
│   │   ├── reducerA.js
│   │   └── reducerB.js
│   ├── action.js
│   ├── actionTypes.js
│   ├── bindActions.js
│   ├── container.js
│   ├── model.js
│   ├── param.js
│   └── store.js  
└── app.js

Store:

Redux中仅有三个store,store中保存应用的装有景况;判别要求转移的情形分配给reducer去管理。

能够有七个reducer,每种reducer去担任一小部分职能,最后将八个reducer合併为叁个根reducer

作用:

  • 维持state树;
  • 提供 getState() 方法获得 state;
  • 提供 dispatch(action) 方法立异 state;
  • 通过 subscribe(listener) 注册监听器。
代码拆分:

1.装置开关所在组件:

// SetContainer.js

import React from 'react';
import {connect} from 'react-redux';
import SetPage from '../pages/SetPage';

class SetContainer extends React.Component {
    render() {
        return (
            <SetPage {...this.props} />
        )
    }
}

export default connect((state) => {

    const { setReducer } = state;
    return {
        setReducer
    }

})(SetContainer);

那是容器组件,将SetPage组件与redux结合起来,当中最要紧的不二秘籍是connect,那个示例中是将setReducer作为质量传给SetPage组件,关于connect的详细解释,请移步到connect()。

2.SetPage组件

import React, {
    Component
} from 'react';
import {
    StyleSheet,
    Text,
    Image,
    ListView,
    TouchableOpacity,
    View,
    Switch,
    InteractionManager,
} from 'react-native';

import Common from '../common/common';
import Loading from '../common/Loading';
import HeaderView from '../common/HeaderView';

import {setText,setTitle} from '../actions/setAction';

export default class SetPage extends Component {
    constructor(props){
        super(props);
        this.state = {
            switchValue: false,
            textValue: false
        }

        this.onValueChange = this.onValueChange.bind(this);
        this.onTextChange = this.onTextChange.bind(this);
    }

    componentDidMount() {
        // console.log(this.props)
    }

    onValueChange(bool) {
        const { dispatch } = this.props;
        this.setState({
            switchValue: bool
        })
        dispatch(setTitle(bool));
    }

    onTextChange(bool) {
        const { dispatch } = this.props;

        this.setState({
            textValue: bool
        });

        dispatch(setText(bool));
    }

    render() {
        return (
            <View>
                <HeaderView
                  titleView= {'设置'}
                  />

                <View>
                    <View style={styles.itemContainer}>
                        <Text style={{fontSize: 16}}>全局设置标题主题</Text>
                        <Switch 
                            onValueChange={this.onValueChange}
                            value={this.state.switchValue}
                        />
                    </View>

                    <View style={styles.itemContainer}>
                        <Text style={{fontSize: 16}}>设置详情页文字主题</Text>
                        <Switch 
                            onValueChange={this.onTextChange}
                            value={this.state.textValue}
                        />
                    </View>
                </View>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    itemContainer:{
        paddingLeft: 20,
        paddingRight: 20,
        height: 40,
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center'
    }
})

能够只看全局设置标题主旨那些方法,设置详细情形页文字颜色和他同理。这里能够清晰的见到,客户切换大旨switch按键的时候,触发的措施:

dispatch(setTitle(bool));

3.我们查阅一下setTitle那一个action的源码:

// setAction.js
import * as types from './actionTypes';

let setTitle = (value) => {
    return (dispatch, getState) => {
        dispatch(changeValue(value))
    }
}

let setText = (text) => {
    return dispatch => {
        dispatch(changeText(text))
    }
}

// 修改标题主题
let changeValue = (titleTheme) => {
    return {
        type: types.CHANGE_SET_SWITCH,
        // 这里将titleTheme状态返回
        titleTheme
    }
}

// 设置详情页内容文字主题
let changeText = (textColor) => {
    return {
        type: types.CHANGE_SET_TEXT,
        textColor
    }
}

export {
    setText,
    setTitle
};

4.action只是担任发送事件,并不会回到三个新的state供页面组件调用,它是在reducer中回到的:

// setReducer.js

import * as types from '../actions/actionTypes';

const initialState = {
    titleTheme: false,
    textColor: false
}

let setReducer = (state = initialState, action) => {

    switch(action.type){
        case types.CHANGE_SET_SWITCH:
            return Object.assign({}, state, {
                titleTheme: action.titleTheme,
            })

        case types.CHANGE_SET_TEXT:
            return Object.assign({}, state, {
                textColor: action.textColor
            })

        default:
            return state;
    }
}

export default setReducer

最简易的reducer,正是遵照初步值和action对象,重回一个新的state,提供给store,那样,页面里能够从store中获取到这一个全局的state,用于更新组件。

大家只是写了何等发送action和接纳action发出newState的,下边来看这几个题目组件是何等和redux结合的。

5.HeaderView组件

/**
 * Created by ljunb on 16/5/8.
 * 导航栏标题
 */
import React from 'react';
import {
    StyleSheet,
    View,
    Text,
    Image,
    TouchableOpacity,
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import Common from '../common/common';
import {connect} from 'react-redux';

class HeaderView extends React.Component {

    constructor(props){
        super(props);

        this.state = {

        }
    }

    render() {
        // 这里,在这里
        const { titleTheme } = this.props.setReducer;
        let NavigationBar = [];

        // 左边图片按钮
        if (this.props.leftIcon != undefined) {
            NavigationBar.push(
                <TouchableOpacity
                    key={'leftIcon'}
                    activeOpacity={0.75}
                    style={styles.leftIcon}
                    onPress={this.props.leftIconAction}
                    >
                    <Icon color="black" size={30} name={this.props.leftIcon}/>
                </TouchableOpacity>
            )
        }

        // 标题
        if (this.props.title != undefined) {
            NavigationBar.push(
                <Text key={'title'} style={styles.title}>{this.props.title}</Text>
            )
        }

        // 自定义标题View
        if (this.props.titleView != undefined) {
            let Component = this.props.titleView;

            NavigationBar.push(
                <Text key={'titleView'} style={[styles.titleView, {color: titleTheme ? '#FFF' : '#000'}]}>{this.props.titleView}</Text>
            )
        }


        return (
            <View style={[styles.navigationBarContainer, {backgroundColor: titleTheme ? 'blue' : '#fff'}]}>
                {NavigationBar}
            </View>
        )
    }
}

const styles = StyleSheet.create({

    navigationBarContainer: {
        marginTop: 20,
        flexDirection: 'row',
        height: 44,
        justifyContent: 'center',
        alignItems: 'center',
        borderBottomColor: '#ccc',
        borderBottomWidth: 0.5,
        backgroundColor: 'white'
    },

    title: {
        fontSize: 15,
        marginLeft: 15,
    },
    titleView: {
        fontSize: 15,
    },
    leftIcon: {
       left: -Common.window.width/2+40,
    },
})


export default connect((state) => {

    const { setReducer } = state;
    return {
        setReducer
    }

})(HeaderView);

其意气风发组件相像运用connect方法绑定了redux,形成了容器组件(container component卡塔 尔(阿拉伯语:قطر‎。

connect真的很主要,请详细查看官方文书档案,上边有链接。

其它不相干的内容忽视,宗旨代码是:

// 拿到全局的state 当有变化的时候,会马上修改
const { titleTheme } = this.props.setReducer;

现实示例2:

图片 10

image.png

利用redux来央浼数据、下拉刷新、上拉加载更加多。

1.首先,封装action。

import * as types from './actionTypes';
import Util from '../common/utils'; 
// action创建函数,此处是渲染首页的各种图片
export let home = (tag, offest, limit, isLoadMore, isRefreshing, isLoading) => {
    let URL = 'http://api.huaban.com/fm/wallpaper/pins?limit=';
    if (limit) URL += limit;
    offest ? URL += '&max=' + offest : URL += '&max=';
    tag ? URL += '&tag=' + encode_utf8(tag) : URL += '&tag='

    return dispatch => {
        // 分发事件  不修改状态   action是 store 数据的唯一来源。
        dispatch(feachHomeList(isLoadMore, isRefreshing, isLoading));
        return Util.get(URL, (response) => {
            // 请求数据成功后
            dispatch(receiveHomeList(response.pins))
        }, (error) => {
            // 请求失败
            dispatch(receiveHomeList([]));
        });

    }

}

function encode_utf8(s) {
    return encodeURIComponent(s);
}

// 我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。
let feachHomeList = (isLoadMore, isRefreshing, isLoading) => {
    return {
        type: types.FETCH_HOME_LIST,
        isLoadMore: isLoadMore,
        isRefreshing: isRefreshing,
        isLoading: isLoading,
    }
}

let receiveHomeList = (homeList) => {
    return {
        type: types.RECEIVE_HOME_LIST,
        homeList: homeList,
    }
}
  • feachHomeList表示正在号令数据的动作;
  • receiveHomeList表示乞请数据完后的动作;
  • dispatch(feachHomeList(isLoadMore, isRefreshing, isLoading));表示分发央求数据的动作;

2.封装reducer函数

import * as types from '../actions/actionTypes';
// 设置初始状态
const initialState = {
    HomeList: [],
    isLoading: true,
    isLoadMore: false,
    isRefreshing: false,
};

let homeReducer = (state = initialState, action) => {

    switch (action.type) {
        case types.FETCH_HOME_LIST:
            return Object.assign({}, state, {
                isLoadMore: action.isLoadMore,
                isRefreshing: action.isRefreshing,
                isLoading: action.isLoading
            })

        case types.RECEIVE_HOME_LIST:
            // 如果请求成功后,返回状态给组件更新数据
            return Object.assign({}, state, {
            // 如果是正在加载更多,那么合并数据
                HomeList: state.isLoadMore ? state.HomeList.concat(action.homeList) : action.homeList,
                isRefreshing: false,
                isLoading: false,
            })

        case types.RESET_STATE: // 清除数据
            return Object.assign({},state,{
                HomeList:[],
                isLoading:true,
            })
        default:
            return state;
    }
}

export default homeReducer;
  • 此地并未拍卖未有更加的多多少的意况。

3.容器组件

import React from 'react';
import {connect} from 'react-redux';
import Home from '../pages/Home';

class HomeContainer extends React.Component {
    render() {
        return (
            <Home {...this.props} />
        )
    }
}

export default connect((state) => {
    const { Home } = state;
    return {
        Home
    }
})(HomeContainer);
  • 此地根本是运用connect函数将Home state绑定到Home组件中,并作为它的props;

4.UI组件

  • 构件挂载诉求数据
...
let limit = 21;
let offest = '';
let tag = '';
let isLoadMore = false;
let isRefreshing = false;
let isLoading = true;
...
componentDidMount() {
    InteractionManager.runAfterInteractions(() => {
      const {dispatch} = this.props;
      // 触发action 请求数据
      dispatch(home(tag, offest, limit, isLoadMore, isRefreshing, isLoading));
    })
}
...
  • 下拉刷新
// 下拉刷新
  _onRefresh() {
    if (isLoadMore) {
      const {dispatch, Home} = this.props;
      isLoadMore = false;
      isRefreshing = true;
      dispatch(home('', '', limit, isLoadMore, isRefreshing, isLoading));
    }
  }
  • 上拉加载越来越多
// 上拉加载
  _onEndReach() {

    InteractionManager.runAfterInteractions(() => {
      const {dispatch, Home} = this.props;
      let homeList = Home.HomeList;
      isLoadMore = true;
      isLoading = false;
      isRefreshing = false;
      offest = homeList[homeList.length - 1].seq
      dispatch(home(tag, offest, limit, isLoadMore, isRefreshing, isLoading));
    })

  }
  • render方法
render() {
    // 这里可以拿到Home状态
    const { Home,rowDate } = this.props;
     tag = rowDate;

    let homeList = Home.HomeList;
    let titleName = '最新';
    return (
      <View>
        <HeaderView
          titleView= {titleName}
          leftIcon={tag ? 'angle-left' : null}
          />
        {Home.isLoading ? <Loading /> :
          <ListView
            dataSource={this.state.dataSource.cloneWithRows(homeList) }
            renderRow={this._renderRow}
            contentContainerStyle={styles.list}
            enableEmptySections={true}
            initialListSize= {10}
            onScroll={this._onScroll}
            onEndReached={this._onEndReach.bind(this) }
            onEndReachedThreshold={10}
            renderFooter={this._renderFooter.bind(this) }
            style={styles.listView}
            refreshControl={
              <RefreshControl
                refreshing={Home.isRefreshing}
                onRefresh={this._onRefresh.bind(this) }
                title="正在加载中……"
                color="#ccc"
                />
            }
            />
        }
      </View>

    );

  }

从这之后,多个大概的Reducer程序完结了,大家有个别计算一下:

  • 全总应用独有二个store,用来保存全部的景色,视图无需本身维护状态。
  • 视图通过connect函数绑定到store,当store状态变化后,store会布告视图刷新。
  • 接触三个action之后,会通过恐怕N个reducers管理,最终根reducer会将全体reducers管理现在的情景合并,然后交到store,store再通报视图刷新。

正文的源码地址: 案例Demo

输入文件 app.js 与顶层组件 react/container.js

这块大家多数保持和事先思路上的意气风发律,用 react-redux 桥接库提供的 Provider 与函数 connect 实现 Redux store 到 React state 的变化。

紧凑的你会在 Provider 的源码中发觉,它最后回到的依然子组件(本例中便是顶层容器组件 “Container“ 卡塔尔国。星星照旧很简单,Container 依然那个Container,只是多了一个 Redux store 对象。

而 Contaier 作为 业务组件 Wrapper 的 高阶组件 ,担负把 Provider 赋予它的 store 通过 store.getState() 获取数据,转而赋值给 state 。然后又依据大家定义的 mapStateToProps 函数按自然的结构将 state 对采用 props 上。 mapStateToProps 函数我们稍后详说。如下所见,这一步关键是 connect 函数干的体力劳动。

//入口文件:app.js
import store from './react/store';
import Container from './react/container'; 
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
ReactDOM.render(
    <Provider store={store}>
        <Container />
    </Provider>,
    document.getElementById("root")
);

//顶层容器组件:react/container.js
import mapStateToProps from './param';
import {connect} from 'react-redux';
import Wrapper from '../components/wrapper';
export default connect(mapStateToProps)(Wrapper);

Reducer:

store想要知道三个action触发后如何转移状态,会实行reducer。reducer是纯函数,根reducer拆分为多个小reducer ,各种reducer去管理与自家有关的state更新

注:不间接改换总体应用的景况树,而是将状态树的每意气风发有的进行拷贝并改良拷贝后的变量,然后将那么些部分重组成一颗新的事态树。行使了数额不可变性(immutable卡塔 尔(阿拉伯语:قطر‎,易于追踪数据变动。别的,还足以追加例如撤除操作等功能。

事务组件 component/Wrapper.js 与 mapStateToProps

那八个模块是整整应用很关键的政工模块。作为二个复杂应用,将 state 上的数量和 actionCreator 合理地分发到各样业务组件中,同期要轻巧维护,是开拓的第大器晚成。

率先,大家统筹 mapStateToProps 函数。供给谨记一点:得到的参数是 connect 函数交给大家的根 state,再次来到的对象是最终 this.props 的结构。

和 Redux 合法示例现在不是过去能比得上的是,大家为了可读性,将分发 action 的函数也席卷进这几个组织中。那也是得益于 bindActions 模块,稍后我们会讲到。

//mapStateToProps:react/param.js
import bindActions from './bindActions';
const mapStateToProps = state => {
    let {demoAPP} = state; // demoAPP 也是 reducer 中的同名函数
    // 分发 action 的函数
    let {initDemoAPP, setDemoAPP} = bindActions;
    // 分发 state 上的数据
    let {isLoading, dataForA, dataForB} = demoAPP;
    let {dataForAA1, dataForAA2, dataForAB} = dataForA; 
    // 返回的对象即为 Wrapper 组件的 this.props
    return {
        initDemoAPP, // Wrapper 组件需要发送一个 action 初始化页面数据
        isLoading, //  Wrapper 组件需要 isLoading 用于展示
        paramsComponentA: {
            dataForA, // 组件 A 需要 dataForA 用于展示
            paramsComponentAA: {
                setDemoAPP, // 组件 AA 需要发送一个 action 修改数据
                dataForAA1,
                dataForAA2
            },
            paramsComponentAB: {
                dataForAB
            }
        },
        paramsComponentB: {
            dataForB,
            paramsComponentBA: {}
        }
    }
}
export default mapStateToProps;

那样,大家那个函数就计划好实行它散发数据和构件行为的任务了。那么,它又该如何“服兵役” 呢?

灵活的你一定察觉到刚刚大家设计的布局中,以 “ params ” 开端的性能既没起到给组件呈现数据的效能,又不曾为组件发送 action 的功能。它们便是我们分发以上二种效率属性的第意气风发。

咱俩先来看看业务组件 Wrapper :

//业务组件组件:components/wrapper.js
import React, { Component } from 'react';
import ComponentA from '../component-a';
import ComponentB from '../component-b';
export default class Example extends Component {
    constructor(props) {
        super(props);
    }
    componentDidMount() {
        this.props.initDemoAPP(); //拉取业务数据
    }
    render() {
        let {paramsComponentA, paramsComponentB, isLoading} = this.props;

        if (isLoading) {
            return (App is loading ...);
        }
        return (
            <div>
                {/* 为组件分发参数 */}
                <ComponentA {...paramsComponentA}/>
                <ComponentB {...paramsComponentB}/>
            </div>
        );
    }
}

现行反革命,param 属性们为大家来得了它扮演的剧中人物:在组件中其实分发数据和措施的特快专递小哥。那样,固然类型越变越大,组件嵌套越多,大家也能在 param.js 模块中,清晰地收看我们的组件结构。需要变动的时候,大家也能比一点也不慢地稳住和改善,而不用对着用之不竭的构件模块梳理父子关系。

言听计用你应有能猜到剩下的子组件们怎么取到数据了,这里限于篇幅就不贴出它们的代码了。

Views:

容器型组件 Container component 和体现型组件 Presentational component卡塔尔

建议是只在最顶层组件(如路由操作卡塔 尔(英语:State of Qatar)里使用 Redux。其他内部组件仅仅是体现性的,全数数据都经过 props 传入。

容器组件 展示组件
Location 最顶层,路由处理 中间和子组件
Aware of Redux
读取数据 从 Redux 获取 state 从 props 获取数据
修改数据 向 Redux 派发 actions 从 props 调用回调函数

Action 模块: react/action.js、react/actionType.js 和 react/bindActions.js

在头里的牵线中,大家提到:三个 ActionCreator 长这样:

const addTodo = text => ({
    type: 'ADD_TODO',
    text
});

而在 Redux 中,真正让其散发叁个 action ,并让 store 响应该 action,依赖的是 dispatch 方法,即:

store.dispatch(addTodo('new todo item'));

相互动作风姿浪漫多,就能化为:

store.dispatch(addTodo('new todo item1'));
store.dispatch(deleteTodo(0));
store.dispatch(compeleteTodo(1));
store.dispatch(clearTodos());
//...

而轻松想到:抽象出多个公用函数来散发 action (这里大致写一下自己的思绪,简化方式并不唯黄金年代卡塔尔

const {dispatch} = store;
const dispatcher = (actionCreators, dispatch) => {
    // ...校验参数
    let bounds = {};
    let keys = Object.keys(actionCreators);
    for (let key of keys) {
        bounds[key] = (...rest) => {
            dispatch(actionCreators[key].apply(null, rest));
        }
    }
    return bounds;
}
//简化后的使用方式
const disp = dispatcher({
    addTodo,
    deleteTodo,
    compeleteTodo
    //...
}, dispatch);
disp.addTodo('new todo item1');
disp.deleteTodo(0);
//...

而精心的 Redux 已经为我们提供了那个方式 —— bindActionCreator

因此,我们的 bindActions.js 模块就借出了 bindActionCreator 来简化 action 的分发:

// react/bindActions.js
import store from './store.js';
import {bindActionCreators} from 'redux';
import * as actionCreators from './action';
let {dispatch} = store;
export default bindActionCreators({ ...actionCreators}, dispatch);

简单想象,action 模块里就是五个个 actionCreator :

// react/action.js
import * as types from '/actionType.js';
export const setDemoAPP = payload => ({
    type: types.SET_DEMO_APP,
    payload
});
// 其他 actionCreators ...

为了越来越好地同盟,大家单独为 action 的 type 划分了四个模块 —— actionTypes.js 里面看起来会不会微小俗:

// react/actionTypes.js
export const SET_DEMO_APP = "SET_DEMO_APP";
// 其他 types ...

Middleware:

中间件是在action被提倡之后,达到reducer从前对store.dispatch方法开展扩充,加强其成效。

举例常用的异步action => redux-thunk、redux-promise、redux-logger等

react/reducers/ 和 react/store.js

日前大家提及,reducer 的效劳正是分别 action type 然后更新 state ,这里不再赘述。可上手实际项目标时候,你会意识 action 类型和对应管理方式多起来会让单个 reducer 快速宏大。

为此,大家就得煞费苦心将其按专门的学业逻辑拆分,以防难以维护。但是怎么把拆分后的 Reducer 组合起来呢 Redux 再一次为大家提供便捷 —— combineReducers 。

唯有纯粹 Reducer 时,想必代码结构你也晓得:

import * as actionTypes from '../actionTypes';
let initState = {
    isLoading: true
};
// 对应 state.demoAPP
const demoAPP = (state = initState, action) => {
    switch (action.type) {
        case actionTypes.SET_DEMO_APP:
            return {
                isLoading: false,
                ...action.payload
            };
        default:
            return state;
    }
}
export default demoAPP; // 把它转交给 createStore 函数

大家最后拿到的 state 结构是:

  • state
    • demoAPP

当有八个 reducer 时:

import * as actionTypes from '../actionTypes';
import { combineReducers } from 'redux';
let initState = {
    isLoading: true
};
// 对应 state.demoAPP
const demoAPP = (state = initState, action) => {
    switch (action.type) {
        case actionTypes.SET_DEMO_APP:
            return {
                isLoading: false,
                ...action.payload
            };
        default:
            return state;
    }
}
// 对应 state.reducerB
const reducerB = (state = {}, action) => {
    switch (action.type) {
        case actionTypes.SET_REDUCER_B:
            return {
                isLoading: false,
                ...action.payload
            };
        default:
            return state;
    }
}
const rootReducer = combineReducers({demoAPP,reducerB});
export default rootReducer;

我们最后收获的 state 结构是:

  • state
    • demoAPP
    • reducerB

或然你已经想到更进一层,把这几个 Reducer 拆分到相应的文本模块下:

// react/reducers/index.js 
import demoAPP from './demoAPP.js';
import reducerB from './reducerB.js';
const rootReducer = combineReducers({demoAPP,reducerB});
export default rootReducer;

紧接着,大家来看 store 模块:

// react/store.js
import rootReducer from './reducers';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
const initialState = {};
const finalCreateStore = compose(
    applyMiddleware(thunk)
)(createStore);
export default finalCreateStore(rootReducer, initialState);

怎么和伪造的不均等?不应有是那样吗:

// react/store.js
import rootReducer from './reducers';
import { createStore } from 'redux';
export default createStore(rootReducer);

此间引进 redux 中间件的概念,你只需了然 redux 中间件的效果与利益便是 在 action 发出现在,给大家三个再加工 action 的机会 就能够了。

干什么要引进 redux-thunk 那当中间件呢?

要明白,我们以前所研讨的都以同盟进程。实际项目中,只要境遇央浼接口的气象(当然不仅仅这种场合卡塔 尔(英语:State of Qatar)就要去管理异步进度。

眼下大家通晓,dispatch 叁个 ActionCreator 会马上回到叁个 action 对象,用以更新数据,而中间件付与我们再管理 action 的机缘。

试想一下,若是大家在此个进程中,发掘 ActionCreator 再次回到的并非三个action 对象,而是七个函数,然后经过这一个函数伏乞接口,响应就绪后,大家再 dispatch 一个 ActionCreator ,此番大家实在回到叁个 action ,然后指引接口重回的数额去改善 state 。 那样一来不就消弭了我们的主题材料呢?

本来,那只是基本思路,关于 redux 的中间件设计,又是叁个妙趣横生的话题,有意思味大家能够再开意气风发篇特地探讨,这里点到甘休。

回到大家的话题,经过

const finalCreateStore = compose(
    applyMiddleware(thunk)
)(createStore);
export default finalCreateStore(rootReducer, initialState);

这么包装一回 store 后,我们就能够愉悦地动用异步 action 了:

// react/action.js
import * as types from './actionType.js';
import * as model from './model.js';
// 同步 actionCreator
export const setDemoAPP = payload => ({
    type: types.SET_DEMO_APP,
    payload
});
// 异步 actionCreator
export const initDemoAPP = () => dispatch => {
    model.getBaseData().then(response => {
        let {status, data} = response;
        if (status === 0) {
            //请求成功且返回数据正常
            dispatch(setDemoAPP(data));
        }
    }, error => {
        // 处理请求异常的情况
    });
}

此地我们用 promise 格局来拍卖央求,model.js 模块如您所想是有个别接口须求promise,就如那样:

export const getBaseData () => {
    return $.getJSON('/someAPI');
}

你也得以参照我们往期介绍的其余方法。

末段,我们再来完备一下事先的流程:

图片 11

redux flow

Redux中store、action、views、reducers、middleware等数据流程图如下:图片 12

3.结语

Redux 的 API 三头手都能数得完,源码更是轻巧,加起来不超过500行。但它给大家端来的,不啻是风度翩翩套复杂应用施工方案,更是 Flux 观念的精短表明。其余,你还能从当中心获得函数式编制程序的意趣。

黄金时代千个观众心中有意气风发千个哈姆Wright,你脑英里的又是哪叁个吧?

简化数据流程图:图片 13

Redux核心:

  • 单纯数据源,即:整个Web应用,独有一个Store,存款和储蓄着富有的多寡【数据结构嵌套太深,数据访谈变得繁缛】,保险一切数据流程是Predictable。
  • 将一个个reducer自上而下一级一流地集结起,最后得到贰个rootReducer。 => Redux通过二个个reducer达成了对任何数据源(object tree卡塔 尔(阿拉伯语:قطر‎的拆除访问和改变。 => Redux通过三个个reducer实现了不可变数据(immutability卡塔尔。
  • 享有数据都以只读的,不能够改改。想要改过只可以经过dispatch(action)来退换state。

参考

《Redux 官方文书档案》
《深入 React 技术栈》

二、Redux源码深入分析

前记— redux的源码相比直观简洁~

Redux概念和API,请直接查看官方斯洛伐克共和国(The Slovak Republic卡塔尔语API和官方中文API

Redux目录结构:

|---src   |---applyMiddleware.js   |---bindActionCreators.js   |---combineReducers.js   |---compose.js   |---createStore.js 定义createStore   |---index.js redux主文件,对外暴光了多少个宗旨API

1
2
3
4
5
6
7
|---src
  |---applyMiddleware.js
  |---bindActionCreators.js
  |---combineReducers.js
  |---compose.js
  |---createStore.js 定义createStore
  |---index.js redux主文件,对外暴露了几个核心API

以下分别是各样文件源码深入解析(带普通话批注):

1) combineReducers.js

  • 真相:组合多少个分支reducer并赶回三个新的reducer,参数也是state和action,进行state的换代管理
  • 开端化:store.getState()的开头值为reducer(initialState, { type: ActionTypes.INIT })
  • Reference:

图片 14

combineReducers() 所做的只是生成叁个函数,这些函数来调用生机勃勃多种reducer,种种reducer遵照它们的key来筛选出state中的风度翩翩有个别数据并管理,然后那几个转换的函数再将具有reducer的结果归拢成二个终极的state对象。

在事实上行使中,reducer中对此state的拍卖是新兴成三个state对象(深拷贝):图片 15

因而在combineReducers中各种小reducers的 nextStateForKey !== previousStateForKey 一定为 true => hasChange也必定为true

那么难题来了,为啥要每一遍都拷贝八个新的state,重返二个新的state呢?
解释:
  1. Reducer 只是风流倜傥对纯函数,它选拔从前的 state 和 action,并回到新的 state。刚初叶容许只有八个 reducer,随着应用变大,把它拆成七个小的 reducers,分别独立地操作 state tree 的例外界分,因为 reducer 只是函数,可以决定它们被调用的依次,传入附加数据,以致编写可复用的 reducer 来管理局地通用义务,如分页器等。因为Reducer是纯函数,由此在reducer内部直接修正state是副成效,而回到新值是纯函数,可相信性巩固,便于追踪bug。
  2. 除此以外由于不可变数据结构总是校订援用,指向同三个数据结构树,实际不是直接改革数据,能够保存任意三个历史场馆,那样就足以变成react diff进而局地刷新dom,也便是react极度急忙的原由。
  3. 因为严苛限制函数纯度,所以各个action做了如何和平会谈会议做什么总是永久的,以至能够把action存到三个栈里,然后逆推出早先的持有state,即react dev tools的行事规律。再谈到react,日常的话操作dom只可以通过副成效,然则react的零零器件都以纯函数,它们连接被动地直接表现store中得内容,约等于说,好的零件,不受外部碰着苦闷,永久是可信赖的,出了bug只可以在外边的逻辑层。那样写好纯的virtual dom组件,交给react管理副功用,很好地分开了关心点。

2) applyMiddleware.js

  • 本质:利用中间件来包装store的dispatch方法,假使有五个middleware则需求接收compose函数来整合,从右到左依次施行middleware
  • Reference:applymiddleware方法、middleware介绍

图片 16Reducer有许多很风趣的中间件,能够参照中间件

3) createStore.js

  • 实质:
  1. 若无需利用中间件,则成立三个分包dispatch、getState、replaceReducer、subscribe多种方法的对象
  2. 若使用中间件,则接纳中间件来包装store对象中的dispatch函数来落到实处更加多的功用
  • createStore.js中代码轻便易读,十分轻便掌握~

(警告)注:

  1. redux.createStore(reducer, preloadedState, enhancer)假如传入了enhancer函数,则赶回 enhancer(createStore)(reducer, preloadedState)假使未传入enhancer函数,则赶回一个store对象,如下:

图片 17

  1. store对象对外拆穿了dispatch、getState、subscribe、replaceReducer方法
  2. store对象通过getState() 获取内部最新state
  3. preloadedState为 store 的起先状态,借使不传则为undefined
  4. store对象通过reducer来更正内部state值
  5. store对象创造的时候,内部会积极性调用dispatch({ type: ActionTypes.INIT })来对当中情状举办早先化。通过断点恐怕日志打字与印刷就足以看来,store对象制造的同有时候,reducer就能够被调用进行初叶化。

Reference:)

假造实际运用中司空见惯接纳的中间件thunk和logger:

  • thunk源码:

图片 18

  • logger源码:

图片 19

一切store包装流程:图片 20

4) bindActionCreators.js

  • 精气神:将有着的action都用dispatch包装,方便调用
  • Reference:

图片 21

5) compose.js

  • 实为:组合四个Redux的中间件
  • Reference:

图片 22

6) index.js

  • 本质:抛出Redux中多少个举足轻重的API函数

三、实例应用Redux

Redux的核情感想:Action、Store、Reducer、UI View合营来贯彻JS中复杂的状态管理,详细解说请查看:Redux功底

React+Redux结合使用的工程目录结构如下:

|—actions    addAction.js    reduceAction.js |—components    |—dialog    |—pagination |—constant |—containers    |---add        addContainer.js        add.less    |—reduce        reduceContainer.js        reduce.less |—reducers    addReducer.js    reduceReducer.js |—setting    setting.js |—store    configureStore.js |—entry    index.js |—template    index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|—actions
   addAction.js
   reduceAction.js
|—components
   |—dialog
   |—pagination
|—constant
|—containers
   |---add
       addContainer.js
       add.less
   |—reduce
       reduceContainer.js
       reduce.less
|—reducers
   addReducer.js
   reduceReducer.js
|—setting
   setting.js
|—store
   configureStore.js
|—entry
   index.js
|—template
   index.html

优势:显明代码重视,缩小耦合,收缩复杂度~~

上边是实际上中国人民解放军海军事工业程高校业程使用中接收react+redux框架进行重构时,总计运用redux时所关联部分标题&&须要在意的点:

1. Store

在成立新的store即createStore时,需求传入由根Reducer、最早化的state树及应用中间件。

1)根Reducer

重构的工程应用代码非常多,不或然让一切state的变动都经过一个reducer来管理。须求拆分为多少个小reducer,最后经过combineReducers来将三个小reducer合并为一个根reducer。拆分reducer时,每一个reducer担当state中风流倜傥部分数据,最终将拍卖后的多少统十分一为全部state。稳重各类reducer只肩负管理全局state中它承当的大器晚成有的。各种reducer的state参数都不可相提并论,分别对应它管理的那部分state数据。

实在工程代码重构中以功用来拆分reducer:图片 23

是es6中指标的写法,各样reducer所担任的state能够改造属性名。

2)initialState => State树

兼备state结构:在Redux应用中,全部state都被保存在一个单风流倜傥对象中,此中满含工程全局state,因而对此全体重构工程而言,提前规划state结构显得非常首要。

尽只怕把state范式化:超过四分之二程序管理的多寡都是嵌套或互相关联的,开采复杂应用时,尽恐怕将state范式化,不设有嵌套。可参照State范式化

2、Action

独一触发矫正state的入口,日常是dispatch差异的action。

API央求尽量都放在Action中,但发送央浼成功中回到数据分歧境况尽量在Reducer中开展拍卖。

  • action.js:

图片 24

  • reducer.js

图片 25

注:

1、倘诺在乞求发送后,要求依照再次回到数据来推断是还是不是供给发送别的央浼或许实践一些非纯函数,那么能够将回来数据分歧情况的拍卖在Action中实行。

2、假使境遇须求错误,需求给客商体现错误原因,如上述reducer代码中errorReason。 要求盘算到是还是不是可能会在提醒中加进DOM成分只怕部分互为操作,因此最棒是将errorReason在action中赋值,最终在reducer中开展多少处理【reducer是纯函数】。

  • action.js

图片 26

3、Reducer

reducer是八个收下旧state和action,重回新state的函数。 (prevState, action) => newState

深深记住要保全reducer纯净,若是传入参数相符,再次回到总括拿到的下一个 state 就确定相通。未有例外情形、未有副作用,未有 API 央求、未有变量修正,单纯推行总括。长久不要在reducer中做这个操作:

a、修正传入参数 b、实行有副功用的操作,如API央浼和路由跳转等 c、调用非纯函数,比如Date.now() 或 Math.random()

1
2
3
a、修改传入参数
b、执行有副作用的操作,如API请求和路由跳转等
c、调用非纯函数,例如Date.now() 或 Math.random()

千古不要涂改旧state!比方,reducer 里永不采用 Object.assign(state, newData),应该运用Object.assign({}, state, newData)。那样才不会覆盖旧的 state。

  • reducer.js:

图片 27

4、View(Container)

渲染界面

a、mapStateToProps

利用mapStateToProps能够获得全局state,不过当前页面只须要该页面包车型地铁所担负部分state数据,因而在给mapStateToProps传参数时,只供给传当前页面所涉嫌的state。由此在相应的reducer中,选取的旧state也是当下页面所涉及的state值。

b、mapDispatchToProps

在mapDispatchToProps中选用bindActionCreators让store中dispatch页面全体的Action,以props的样式调用对应的action函数。

具备的 dispatch action 均由 container 注入 props 格局完结。

c、connect ( react-redux )

react-redux 提供的 connect() 方法将零部件连接到 Redux,将运用中的任何三个零零件connect()到Redux Store中。被connect()包装好的零部件都得以拿走一些主意作为组件的props,并且可以拿到全局state中的任何内容。

connect中封装了shouldComponentUpdate方法图片 28

风度翩翩旦state保持不改变那么并不会招致重复渲染的标题,内部组件依旧利用mapStateToProps方法采用该零器件所须要的state。需求小心的是:单独的成效模块不能使用其余模块的state.

d、bind

在constructor中bind全部event handlers => bind方法会在历次render时都再一次再次来到一个指向性内定功能域的新函数

  • container.js

图片 29

四、总结

整篇小说主若是源码驾驭和现实品种接受中全方位Redux管理state的流水线,小编对Redux有了越来越深等级次序的敞亮。

Redux+React已普遍应用,期望在现在的施用进度中,有更多更浓烈的明白~

如有错误,应接指正 (~ ̄▽ ̄)~

参照链接:

  • redux连串源码解析:
  • redux github:
  • redux剖析:

    1 赞 收藏 评论

图片 30

本文由9159.com发布于前端,转载请注明出处:参考链接,最近静下心细读源码

关键词: