react中redux到react-redux

什么是redux


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

image.png

什么时候使用redux


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

  • 某个状态需要在任何地方都可以拿到

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

  • 一个组件需要改变另一个组件的状态

如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。

基础和概念


state

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

{
  loginName: '',
  visibilityFilter: 'SHOW_COMPLETED'
}

这个对象就像 “Model”,区别是它并没有 setter(修改器方法)。因此其它的代码不能随意修改它,造成难以复现的 bug。

action

  • 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 方法来达到你想要的目的。

image

参数

  • ...middlewares (arguments): 遵循 Redux middleware API 的函数。每个 middleware 接受 StoredispatchgetState 函数作为命名参数,并返回一个函数。该函数会被传入 被称为 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)

<Provider store>

<Provider store> 使组件层级中的 connect() 方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 <Provider> 中才能使用 connect() 方法。

属性

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

示例

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

redux的使用先讲到这里,如有错误之处希望大家积极留言!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容

  • 从上周开始一直在看 Redux 相关的文档和资料,一开始一连几天都没有进展,只能一边看文档一边默默 debug,到...
    aJIEw阅读 2,333评论 2 11
  • 系列文章:Redux 入门(本文)Redux 进阶番外篇: Vuex — The core of Vue appl...
    6ed7563919d4阅读 5,274评论 4 44
  • Redux入门学习系列教程(一)Redux入门学习系列教程(二)Redux入门学习系列教程(三)Redux入门学习...
    光强_上海阅读 9,876评论 1 36
  • 学习必备要点: 首先弄明白,Redux在使用React开发应用时,起到什么作用——状态集中管理 弄清楚Redux是...
    贺贺v5阅读 8,885评论 10 58
  • Redux入门学习系列教程(一)Redux入门学习系列教程(二)Redux入门学习系列教程(三)Redux入门学习...
    光强_上海阅读 2,605评论 0 9