redux 浅谈

什么是redux


Redux 是 JavaScript 状态容器,提供可预测化的状态管理。html

何时使用redux


  • 某个组件的状态,须要共享react

  • 某个状态须要在任何地方均可以拿到redux

  • 一个组件须要改变全局状态api

  • 一个组件须要改变另外一个组件的状态promise

若是你的UI层很是简单,没有不少互动,Redux 就是没必要要的,用了反而增长复杂性。bash

基础和概念


stateapp

当使用普通对象来描述应用的 state 时:dom

{
  loginName: '',
  visibilityFilter: 'SHOW_COMPLETED'
}
复制代码

这个对象就像 “Model”,区别是它并无 setter(修改器方法)。所以其它的代码不能随意修改它,形成难以复现的 bug。异步

actionide

  • Action 是把数据从应用传到 store 的有效载荷。

  • 它是 store 数据的惟一来源。

  • 要想更新 state 中的数据,你须要发起一个 action

Action 就是一个普通 JavaScript 对象,像这样:

{type: 'SET_NAME', text: 'tom'},
{type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL'}
复制代码

强制使用 action 来描述全部变化带来的好处是能够清晰地知道应用中到底发生了什么。若是一些东西改变了,就能够知道为何变。action 就像是描述发生了什么的指示器。

注:

在redux中约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动做。多数状况下,type 会被定义成字符串常量。当应用规模愈来愈大时,建议使用单独的模块或文件来存放 action。

reducer

把 action 和 state 串起来,开发一些函数,这就是 reducer。

reducer 只是一个接收 state 和 action,并返回新的 state 的函数。

function loginName(state = '', action) {
  if (action.type === 'SET_NAME') {
    return action.filter;
  } else {
    return state;
  }
}

function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter;
  } else {
    return state;
  }
}

复制代码

再开发一个 reducer 调用这两个 reducer,进而来管理整个应用的 state:

function reducers(state = {}, action) {
  return {
    loginName: loginName(state.loginName, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  };
}

复制代码

注:

reducer 纯净很是重要。永远不要在 reducer 里作这些操做:

  • 修改传入参数;

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

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

每一个 reducer 只负责管理全局 state 中它负责的一部分。每一个 reducer 的 state 参数都不一样,分别对应它管理的那部分 state 数据。

这差很少就是 Redux 思想的所有。redux 就是提供一些简单的工具来简化这种模式。

redux 三大原则


  • 单一数据源

    整个应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于惟一一个 store 中。

  • State 是只读的

    惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。在 default 状况下返回旧的 state。遇到未知的 action 时,必定要返回旧的 state。不要使用 Object.assign(state, newData),应该使用 Object.assign({}, state, newData)

  • 使用纯函数来执行修改

    为了描述 action 如何改变 state tree ,你须要编写 reducers

redux API


createStore(reducer, [preloadedState], enhancer)

建立一个 Redux store来以存放应用中全部的 state。

应用中应有且仅有一个 store。

参数

  • reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树

  • preloadedState(any): 初始时的 state。若是你使用 combineReducers建立 reducer,它必须是一个普通对象,与传入的 keys 保持一样的结构。不然,你能够自由传入任何 reducer 可理解的内容。

  • enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 类似,它也容许你经过复合函数改变 store 接口。

返回值

Store: 保存了应用全部 state 的对象。

Store

Store 就是用来维持应用全部的state 树 的一个对象。

Store 不是类。它只是有几个方法的对象。 要建立它,只须要把根部的 reducing 函数 传递给 createStore

Store 方法 介绍 参数 返回值
getState() 获得state -- (any): 应用当前的 state 树。
dispatch 分发 action。这是触发 state 变化的唯一途径。 action (Object) (Object): 要 dispatch 的 action。
subscribe 一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。 listener (Function): 每当 dispatch action 的时候都会执行的回调。state 树中的一部分可能已经变化。你能够在回调函数里调用 getState() 来拿到当前 state。 (Function): 一个能够解绑变化监听器的函数。
replaceReducer(nextReducer) 替换 store 当前用来计算 state 的 reducer。这是一个高级 API。只有在你须要实现代码分隔,并且须要当即加载一些 reducer 的时候才可能会用到它。 reducer (Function) store 会使用的下一个 reducer。 ---

combineReducers(reducers)

combineReducers 辅助函数的做用是,把一个由多个不一样 reducer 函数做为 value 的 object,合并成一个最终的 reducer 函数。

合并后的 reducer 能够调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。由 combineReducers() 返回的 state 对象,会将传入的每一个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。

rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})
// rootReducer 将返回以下的 state 对象
{
  potato: {
    // ... potatoes, 和一些其余由 potatoReducer 管理的 state 对象 ... 
  },
  tomato: {
    // ... tomatoes, 和一些其余由 tomatoReducer 管理的 state 对象,好比说 sauce 属性 ...
  }
}
复制代码

参数

reducers (Object): 一个对象,它的值(value)对应不一样的 reducer 函数,这些 reducer 函数后面会被合并成一个。

返回值

(Function):一个调用 reducers 对象里全部 reducer 的 reducer,而且构造一个与 reducers 对象结构相同的 state 对象。

注:

每一个传入 combineReducers 的 reducer 都需知足如下规则:

  • 全部未匹配到的 action,必须把它接收到的第一个参数也就是那个 state 原封不动返回。

  • 永远不能返回 undefined。当过早 return 时很是容易犯这个错误,为了不错误扩散,遇到这种状况时 combineReducers 会抛异常。

  • 若是传入的 state 就是 undefined,必定要返回对应 reducer 的初始 state。根据上一条规则,初始 state 禁止使用 undefined。使用 ES6 的默认参数值语法来设置初始 state 很容易,但你也能够手动检查第一个参数是否为 undefined。

示例

//reducers/todos.js
export default function todos(state = [], action) {
  switch (action.type) {
  case 'ADD_TODO':
    return state.concat([action.text])
  default:
    return state
  }
}
//reducers/counter.js
export default function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1
  case 'DECREMENT':
    return state - 1
  default:
    return state
  }
}
//reducers/index.js
import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'

export default combineReducers({
  todos,
  counter
})
//App.js
import { createStore } from 'redux'
import reducer from './reducers/index'

let store = createStore(reducer)
console.log(store.getState())
// {
//   counter: 0,
//   todos: []
// }

store.dispatch({
  type: 'ADD_TODO',
  text: 'Use Redux'
})
console.log(store.getState())
// {
//   counter: 0,
//   todos: [ 'Use Redux' ]
// }
复制代码

applyMiddleware(...middlewares)

使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式。Middleware 可让你包装 store 的 dispatch 方法来达到你想要的目的。

参数

  • ...middlewares (arguments): 遵循 Redux middleware API 的函数。每一个 middleware 接受 Store 的 dispatch 和 getState 函数做为命名参数,并返回一个函数。该函数会被传入 被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数能够直接调用 next(action),或者在其余须要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法做为 next 参数,并借此结束调用链。因此,middleware 的函数签名是 ({ getState, dispatch }) => next => action

使用

import { applyMiddleware, createStore, compose } from 'redux'
import reducers from './reducers'
import {createLogger} from 'redux-logger'
import middlePromise from 'redux-promise'
// import thunk from 'redux-thunk'

// 模拟 logger
const logger = store => next => action =>{
  console.log('prev state',store.getState())
  console.log('dispatch',action);

  let result = next(action);

  console.log('next state',store.getState());

  return result;
}

const  store = createStore(reducers, applyMiddleware(middlePromise, logger))

export default store

// 异步
import { applyMiddleware, createStore, compose } from 'redux'
import reducers from './reducers'
import {createLogger} from 'redux-logger'
// import middlePromise from 'redux-promise'
import thunk from 'redux-thunk'

// 模拟 logger
const logger = store => next => action =>{
  console.log('prev state',store.getState())
  console.log('dispatch',action);
  let result = next(action);

  console.log('next state',store.getState());

  return new Promise((resolve, reject) => {
    resolve(result)
  });
}

const  store = createStore(reducers, compose(applyMiddleware(thunk, logger)))

export default store
复制代码

注:

  • 有的中间件有次序要求,使用前要查一下文档

bindActionCreators(actionCreators, dispatch)

把一个 value 为不一样 action creator 的对象,转成拥有同名 key 的对象。同时使用 dispatch对每一个 action creator 进行包装,以即可以直接调用它们。

参数

  1. actionCreators (Function or Object): 一个 action creator,或者一个 value 是 action creator 的对象。

  2. dispatch (Function): 一个由 Store实例提供的 dispatch函数。

返回值

(Function or Object): 一个与原对象相似的对象,只不过这个对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数。若是传入一个单独的函数做为 actionCreators,那么返回的结果也是一个单独的函数。

示例在react-redux中讲解

compose(...functions)

从右到左来组合多个函数。

当须要把多个store 加强器 依次执行的时候,须要用到它。

参数

(arguments): **须要合成的多个函数。预计每一个函数都接收一个参数。它的返回值将做为一个参数提供给它左边的函数,以此类推。**例外是最右边的参数能够接受多个参数,由于它将为由此产生的函数提供签名。(compose(funcA, funcB, funcC) 形象为 compose(funcA(funcB(funcC()))))

返回值

(Function): 从右到左把接收到的函数合成后的最终函数。

react-redux 基本使用


React-Redux 将全部组件分红两大类:UI 组件(presentational component)和容器组件(container component)。

UI 组件有如下几个特征。

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 没有状态(即不使用this.state这个变量)
  • 全部数据都由参数(this.props)提供
  • 不使用任何 Redux 的 API

示例

const Title = props => <h1>{props.title}</h1>;
复制代码

容器组件

  • 负责管理数据和业务逻辑,不负责 UI 的呈现
  • 带有内部状态
  • 使用 Redux 的 API

React-Redux 规定,全部的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。

connect()

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来。

示例

import { connect } from 'react-redux'

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(Title)
复制代码

connect方法接受两个参数:mapStateToPropsmapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操做映射成 Action。

connect参数 介绍 参数
mapStateToProps mapStateToProps是一个函数。它的做用就是像它的名字那样,创建一个从(外部的)state对象到(UI 组件的)props对象的映射关系。 第一个永远是state;第二个参数ownProps:表明容器组件的props对象
mapDispatchToProps 用来创建 UI 组件的参数到store.dispatch方法的映射 dispatchownProps(容器组件的props对象)两个参数

示例

const mapStateToProps = (state, ownProps) => {
 return {
   state: state,
   title: state.increment + ownProps.title
 }
}
const mapDispatchToProps = (dispatch, ownProps) => {
 return {
   actions: bindActionCreators(actionCreators, dispatch),
   ownPropsClick: () => {
     dispatch(actionCreators.increment())
     console.log(ownProps.title)
   }
 }
}

// mapDispatchToProps 也能够是一个对象
// const mapDispatchToProps = {
//   ownPropsClick: (filter) => {
//     return {
//       type: 'INCREMENT',
//       filter: filter
//     }
//   }
// }

export default connect(mapStateToProps, mapDispatchToProps)(Test)
复制代码

使组件层级中的 connect() 方法都可以得到 Redux store。正常状况下,你的根组件应该嵌套在 中才能使用 connect() 方法。

属性

  • store (Redux Store): 应用程序中惟一的 Redux store 对象
  • children (ReactElement) 组件层级的根组件。

示例

ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  rootEl
)
复制代码

redux的使用先讲到这里,若有错误之处但愿你们积极留言!