模块开发之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
store
是redux
实例,state
是store
管理的数据,通过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 的用法
推荐文章
Redux 入门教程(一):基本用法
Redux 入门教程(二):中间件与异步操作
Redux 入门教程(三):React-Redux 的用法