模块开发之Redux基础入门(六)

模块开发之Redux基础入门(六)

核心思想

redux是数据存储和管理的工具。
Redux核心思想是强制使用action对象去更新state数据,整个应用只能一个store,store是管理state应用状态的管理者,整个应用也只有一个state,使用包含更改state数据的action,通过store.dispatch派发到reducer操作state.
React里每个组件都有自己的state,因为大型应用中组件很多,管理很多state会很麻烦,React 依旧把处理 state 中数据的问题留给了你。redux其实就是对state的管理。

引入模块

import { createStore } from 'redux'

核心概念

Store

Store是保存数据的地方,用于管理state,它有3个方法
getState:获得它管理的state,Redux里禁止直接修改state里状态数据。
dispatch:派发修改state数据的action。
subscribe:设置监听函数,一旦 State 发生变化,就自动执行这个函数。

Redux里使用createStore创建store。
语法:createStore(reducer[,initial_state][,applyMiddleware]);
reducer:这是一个纯函数,函数作用是计算action与state,返回新的state。
initial_state:可选参数,state的初始值。
applyMiddleware:可选参数,一个中间件,如果initial_state不填,则applyMiddleware就为第二个参数,否则为第三个参数。

state

保存应用的整个数据。整个应用只有一个state。即通过store.getState()方法获得。

action

action是一个包含type属性的普通js对象,type属性强制要求,传递对state修改的数据载体。

action creator

action的创建者,我觉得它只是一个概念上东西,
比如一般的action对象如下:

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

action creator将对象封装一个方法里,类似工厂模块,理解这个概念对理解redux用处并不大,我觉得不应该单独把action creator拿出来讲。因为一般我们为了取action方便,都会重构这个对象,封装到一个方法里。

Reducer

是计算state和action结果并返回新state的逻辑部分。换句话说,Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。

详细介绍

actionCreator

先从actionCreator说起,它就是一个函数,返回包含type属性的普通对象。
例如

var actionCreator = function() {
    return {
        type: 'AN_ACTION'
    }
}

返回的对象叫action,一般约定 action 是一个拥有 type 属性的对象。
按 type 决定如何处理 action。当然,action 依旧可以拥有其他属性,你可以任意存放想要的数据。

store

action 告诉我们发生了什么,同时我们还是更新数据。redux帮我们解决了这些问题。Redux 就是一个“容纳状态的容器”
- 如何在应用程序的整个生命周期内维持所有数据?
以你想要的方式维持这些数据,例如 JavaScript 对象、数组、不可变数据,等等。把应用程序的状态数据交给了 Redux,
- 如何修改这些数据?
我们使用 reducer 函数修改数据,Reducer 函数只是一个纯函数,它接收应用程序的当前状态以及发生的 action,然后返回修改后的新状态
- 如何把数据变更传播到整个应用程序?
使用订阅者来监听状态的变更情况。
总之 Redux 提供了:

  • 存放应用程序状态的容器
  • 一种把 action 分发到状态修改器的机制,也就是 reducer 函数
  • 监听状态变化的机制

把 Redux 实例称为 store
创建store语法var store = createStore(reducer函数)

reducer

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

var reducer = function (state, action) {
    console.log('reducer was called with state', state, 'and action', action)
}

而在创建Redux实例store时,绑定了处理action处理函数。
如下

var store = createStore(reducer);

输出为:Reducer was called with args [ undefined, { type: '@@redux/INIT' } ]
state没初始化则为undefined,action默认值为{ type: '@@redux/INIT' }

常见模块:
- 在 reducer 里用 switch 来响应对应的 action
- -用 switch 的时候, 永远 不要忘记放个 “default” 来返回 “state”,否则可能返回“undefined

例如

var reducer = function (state = {}, action) {
    switch (action.type) {
        case 'ADD':
            return {
                ...state,
                message: action.value
            }
        default:
            return state;
    }
}

state

storeredux实例,statestore管理的数据,通过store.getState()获得状态数据。
比如

var reducer = function (state = {}, action) {
    console.log('reducer was called with state', state, 'and action', action)

    return state;
}

reducer 函数指定了state默认值,就不会出现undefined。
到目前为止,我们都还没有得到一个新 state, 因为我们还没有真的派发过任何 action

使用combineReducers拆分reducer

刚项目越繁杂,只有一个 reducer 是 hold 不住我们整个应用中所有 action 操作的。redux提供了combineReducers,用于合并分散的reducer函数成一个大函数。

原理
combineReducers 接收一个对象并返回一个函数,当 combineReducers 被调用时,它会去调用每个reducer,并把返回的每一块 state 重新组合成一个大 state 对象。
例如

var userReducer = function (state = {}, action) {
    console.log('userReducer was called with state', state, 'and action', action)

    switch (action.type) {
        // etc.
        default:
            return state;
    }
}
var itemsReducer = function (state = [], action) {
    console.log('itemsReducer was called with state', state, 'and action', action)

    switch (action.type) {
        // ....
        default:
            return state;
    }
}
import { createStore, combineReducers } from 'redux'

var reducer = combineReducers({
    user: userReducer,
    items: itemsReducer
})

user是state里属性key,userReducer函数返回的是属性值。
最终得到的state状态数据是{ user: {}, items: [] }

ES6 允许直接写入变量和函数,作为对象的属性和方法,例如上面的修改如下:

var reducer = combineReducers({
    userReducer,
    itemsReducer
})

ES6规定,属性名为变量名, 属性值为变量的值。上面的state有属性为userReducer,属性值userReducer返回值。就是 State 的属性名必须与子 Reducer 同名。
最终得到的state状态数据是{ userReducer: {}, userReducer: [] }
其实上面最终类似如下

function reducer(state = {}, action) {
  return {
    userReducer:userReducer: userReducer:userReducer(state.userReducer:userReducer, action),
    itemsReducer: itemsReducer(state.itemsReducer, action),
  }
}

dispatch

dispatch 函数是 Redux 提供的,并且它会将 action 传递给任何一个 reducer,dispatch 函数本质上是 Redux 的实例的属性 dispatch。

同步派发

派发的action最终会到reducer函数里,根据switch找到指定type,处理数据。
例如

var reducer = function (state = [], action) {
    switch (action.type) {
        case 'SET_NAME':
            return [
                ...state,
                action.item
            ]
        default:
            return state;
    }
}
store.dispatch({
        type: 'SET_NAME',
        name: name
    })

异步派发

Action 发出以后,Reducer 立即算出 State,这叫做同步;Action 发出以后,过一段时间再执行 Reducer,这就是异步。
异步执行需要中间件。在 Redux 中,中间件是纯粹的函数
通常来说中间件是在某个应用中 A 和 B 部分中间的那一块,
中间件可以把 A 发送数据到 B 的形式从
A -----> B
变成:
A ---> middleware 1 ---> middleware 2 ---> middleware 3 --> ... ---> B
有明确的使用方法并且严格的遵循以下格式:

var anyMiddleware = function ({ dispatch, getState }) {
        return function(next) {
            return function (action) {
                // 你的中间件业务相关代码
            }
        }
    }

中间件由三个嵌套的函数构成(会依次调用):

  • 1) 第一层向其余两层提供分发函数和 getState 函数
    (因为你的中间件或 action creator 可能需要从 state 中读取数据)
  • 2) 第二层提供 next 函数,它允许你显式的将处理过的输入传递给下一个中间件或 Redux,(这样 Redux 才能调用所有 reducer)。
  • 3) 第三层提供从上一个中间件或从 dispatch 传递来的 action, 这个 action 可以调用下一个中间件(让 action 继续流动) 或者 以想要的方式处理 action。

自定义一个中间件

var thunkMiddleware = function ({ dispatch, getState }) {
    // console.log('Enter thunkMiddleware');
    return function(next) {
        // console.log('Function "next" provided:', next);
        return function (action) {
            // console.log('Handling action:', action);
            return typeof action === 'function' ?
                action(dispatch, getState) :
                next(action)
        }
    }
}

使用辅助函数:applyMiddleware让中间件在redux中生效,applyMiddleware接收所有中间件作为参数,返回一个供 Redux createStore 调用的函数。当最后这个函数被调用时,它会产生一个 Store 增强器,用来将所有中间件应用到 Store 的 dispatch 上。

const store = createStore(reducer,applyMiddleware(thunkMiddleware))

或者
const finalCreateStore = applyMiddleware(thunkMiddleware)(createStore)
const store = finalCreateStore(reducer)

常用的中间件都有现成的,只要引用别人写好的模块即可,不需要重复定义。
日志中间件

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();
const store = createStore(
  reducer,
  applyMiddleware(logger)
);

这里有两点需要注意:

  • createStore方法可以接受整个应用的初始状态作为参数,那样的话,applyMiddleware就是第三个参数了。
const store = createStore(
  reducer,
  initial_state,
  applyMiddleware(logger)
);

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

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

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

redux-thunk 中间件

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

// Note: this API requires redux@>=3.1.0
const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

使用redux-thunk中间件,改造store.dispatch,使得后者可以接受函数作为参数

React-redux框架

因为 Redux 是和 React 完全耦合的,所以我们需要使用 react-redux 来方便我们使用。

引入框架

import { Provider, connect } from 'react-redux'

Provider和connect2个重要概念

Provider包裹App组件,那组件内部都可以使用store上存储的数据,即提供的是一个顶层容器的作用,实现store的上下文传递。
使用方式也挺简单:

import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

connect是组件连接,
connect是一个高阶函数,首先传入mapStateToProps、mapDispatchToProps,然后返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个经过包裹的Connect组件.
所以我们经常用双小括号,如下:

cont containerComponent = connect(mapStateToProps, mapDispatchToProps)(UIComponent)

引用

阮一峰老师解释的很好,下面引用他的内容
React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。
UI 组件有以下几个特征。

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

容器组件的特征恰恰相反。
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API

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

为了定义业务逻辑,需要给出下面两方面的信息。

  • (1)输入逻辑:外部的数据(即state对象)如何转换为 UI 组件的参数
  • (2)输出逻辑:用户发出的动作如何变为 Action 对象,从 UI 组件传出去。

connect的API如下:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

连接 React 组件与 Redux store。
连接操作不会改变原来的组件类。
反而返回一个新的已与 Redux store 连接的组件类。
mapStateToProps:负责输入逻辑,即将state映射到 UI 组件的参数(props)。
mapDispatchToProps:后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
参考这里Redux 入门教程(三):React-Redux 的用法

react-redux里api

推荐文章
Redux 入门教程(一):基本用法
Redux 入门教程(二):中间件与异步操作
Redux 入门教程(三):React-Redux 的用法

猜你喜欢

转载自blog.csdn.net/achenyuan/article/details/80726813
今日推荐