Redux笔记

Redux笔记

React 入门实例教程
React 渲染机制解析
Redux 入门教程
在React中使用Redux
React性能优化 – 利用React-Redux
对于react-redux的理解梳理
react-redux流程与实现分析
跟着例子一步步学习redux+react-redux
React高级篇(一)从Flux到Redux,react-redux

环境搭建

安装create-react-app:cnpm install -g create-react-app

创建react应用:create-react-app reactlearning

安装redux、react-redux等依赖:

cnpm install --save redux react-redux redux-thunk
npm install --save-dev redux-logger

redux存储和处理数据,react-redux帮助你完成数据订阅,redux-thunk可以帮你实现异步action,redux-logger是redux的日志中间件。

  • babel-preset-env根据配置环境计算babel对代码填充何种等级的polyfill,已包括es2015的配置,但没有包括stage-x。官方首推。
  • babel-preset-[stage-0、stage-1、stage-2、stage-3]与ES7相关的配置;
  • 除此之外,Babel还可以有其他扩展,比如我们希望使用JSX语法,这时需要引入babel-preset-react处理器。

Flux

Flux框架也是一种MVC框架,不同于传统的MVC,它采用单向数据流,不允许Model和Control互相引用。Flux框架大致如下:

  • Actions: 驱动Dispatcher发起改变
  • Dispatcher: 负责分发动作(事件)
  • Store: 储存数据,处理数据
  • View: 视图部分

Dispatcher只会暴露一个函数-dispatch,接受Action为参数,发起动作。如果需要增加新功能,不需要改变或者增加接口,只需增加Action类型。

Store 一般会继承EventEmitter,实现事件监听,发布,卸载。需要将store注册到Dispatcher实例上才能够发挥作用。

Redux特点

相比Flux,Redux有如下两个特点:

  1. 在整个应用只提供一个Store,它是一个扁平的树形结构,一个节点状态应该只属于一个组件。
  2. 不允许修改数据。即不能修改老状态,只能返回一个新状态。

Redux 的设计思想很简单

(1)Web 应用是一个状态机,视图与状态是一一对应的。
(2)所有的状态,保存在一个对象里面。

Redux数据流如下:

view dispatch一个action后,通过对应reducer处理,然后更新store,最终views根据store数据的改变重新渲染界面。

不同于 Flux ,Redux 并没有 dispatcher 的概念(Store已经集成了dispatch方法,所以不需要Dispatcher)。它依赖纯函数来替代事件处理器,这个纯函数叫做Reducer。Reducer封装了处理数据的逻辑。

纯函数是函数式编程的概念,必须遵守以下一些约束:

  • 不得改写参数
  • 不能调用系统 I/O 的API
  • 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果

React整个的渲染机制就是在state/props发生改变的时候,重新渲染所有的节点,构造出新的虚拟Dom tree跟原来的Dom tree用Diff算法进行比较,得到需要更新的地方在批量造作在真实的Dom上,由于这样做就减少了对Dom的频繁操作,从而提升的性能。

使用redux

使用redux的方法主要是三步曲:创建store、创建action、创建reducer。而在这之后才是与业务或者组件相关的数据处理和展示。

创建Store

Store 就是保存数据的地方,整个应用只能有一个 Store。

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

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

上面代码中,createStore函数接受reducer函数作为参数,返回新生成的 Store 对象。

Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State

Store是用来管理state的单一对象。其中有三个方法:

  • store.getState():获取state。可以用该函数获取经过reducer返回的新的state;
  • store.dispatch(action):发出操作,更新state。action内有操作的类型,可以触发不同的对state的更新;
  • store.subscribe(listener):监听变化,当state发生更新时,就可以在这个函数的回调中监听。store.subscribe方法返回一个函数,调用这个函数就可以解除监听。

Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。

创建action

State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。

Action 是一个普通的 JavaScript对象。其中的type属性是必须的,表示 Action 的名称。

const action = {
  type: 'ADD',
  payload: 'Learn Redux'
};

View层通过store.dispatch触发动作:

onIncrement() {
   store.dispatch(action);
}

dispatch 在 swtich 里面会识别action的 type 字段,能够识别出来的操作才会执行对 appState 的修改。

创建reducer

Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。

Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。

const reducer = function (state, action) {
  // ...
  return new_state;
};

reducer是一个会对不同action做出不同操作的函数。在没有任何操作情况下,我们返回初始的state。我们不直接去改变state的值,而是返回一个新的对象,保持state的唯一性。

const reducer = (state, action) => {
  switch (action.type) {
    case 'ADD':
      return Object.assign({}, state, {
        userName: action.payload
      });
    default:
      return state;
  }
}

实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch方法会触发 Reducer 的自动执行。

由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,请参考下面的写法。

// State 是一个对象
function reducer(state, action) {
  return Object.assign({}, state, { thingToChange });
  // 或者
  return { ...state, ...newState };
}

// State 是一个数组
function reducer(state, action) {
  return [...state, newItem];
}

最好把 State 对象设成只读。你没法改变它,要得到新的 State,唯一办法就是生成一个新对象。这样的好处是,任何时候,与某个 View 对应的 State 总是一个不变的对象。

reducer拆分

Reducer 函数负责生成 State。由于整个应用只有一个 State 对象,包含所有数据,对于大型应用来说,这个 State 必然十分庞大,导致 Reducer 函数也十分庞大。

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

上面代码中,Reducer 函数被拆成了三个小函数,每一个负责生成对应的属性。

Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。

import { combineReducers } from 'redux';

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

export default todoApp;

上面的代码通过combineReducers方法将三个子 Reducer 合并成一个大的函数。

这种写法有一个前提,就是 State 的属性名必须与子 Reducer 同名。如果不同名,就要采用下面的写法。

const reducer = combineReducers({
  a: doSomethingWithA,
  b: processB,
  c: c
})

// 等同于
function reducer(state = {}, action) {
  return {
    a: doSomethingWithA(state.a, action),
    b: processB(state.b, action),
    c: c(state.c, action)
  }
}

总之,combineReducers()做的就是产生一个整体的 Reducer 函数。该函数根据 State 的 key 去执行相应的子 Reducer,并将返回结果合并成一个大的 State 对象。

中间件的用法

import { applyMiddleware, createStore } from 'redux';
import { createLogger } from 'redux-logger';

const store = createStore(
  reducer,
  applyMiddleware(createLogger)
);

上面代码中,redux-logger提供一个生成器createLogger,可以生成日志中间件logger。然后,将它放在applyMiddleware方法之中,传入createStore方法,就完成了store.dispatch()的功能增强。

这里有两点需要注意:

(1)createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。

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

(2)中间件的次序有讲究。

const store = createStore(
  reducer,
  applyMiddleware(thunk, promise, logger)
);

上面代码中,applyMiddleware方法的三个参数,就是三个中间件。有的中间件有次序要求,使用前要查一下文档。比如,logger就一定要放在最后,否则输出结果会不正确。

react-redux

redux其实是一个通用的库,它不只针对react,还可以用到其它的像vue等库。因此react要想完美的应用redux,还需要封装一层,react-redux就是此作用。

React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

UI 组件有以下几个特征。

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

容器组件的特征恰恰相反。

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

UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

react-redux库提供了一个react组件Provider和一个方法connect。下面的代码是react-redux项目的一般写法。

<Provider store={store} >
    <div style={{height: '100%'}}>
        <Handler/>
    </div>
</Provider>

Provider组件相当于一个外壳,将store传入,并提供getChildContext方法,让后续的子组件可以通过context取到store对象。

connect方法返回一个函数,此函数的功能是创建一个connect组件包在WrappedComponent组件外面,connect组件复制了WrappedComponent组件的所有属性,并通过redux的subscribe方法注册监听,当store数据变化后,connect就会更新state,然后通过mapStateToProps方法选取需要的state,如果此部分state更新了,connect的render方法就会返回新的组件。

export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
  ...
  return function wrapWithConnect(WrappedComponent) {
    ...
  }
}

React-Redux的connect其实会自动做一个对props的优化比较。过程如下:

这里写图片描述

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

function mapStateToProps(state) {
  return {
    value: state.count
  }
}

function mapDispatchToProps(dispatch) {
  return {
    onIncreaseClick: () => dispatch(increaseAction)
  }
}

// Action Creator
const increaseAction = { type: 'increase' }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值