一、認(rèn)識Redux
安裝
npm install --save redux?
npm install --save react-redux
一句話理解Redux
應(yīng)用中的所有state都以一個對象數(shù)樹的方式存儲在一個單一的store中,唯一改變state的方法就是觸發(fā)action,一個描述發(fā)生什么的對象,為了描述action如何改變state樹,需要編寫reducers。
什么是Action?
Action本質(zhì)上是JavaScript的普通對象,action內(nèi)必須使用一個字符串類型的type字段來表示將要執(zhí)行的動作。多數(shù)情況下type會被定義成字符串常量。我們應(yīng)該盡量減少在actin中傳遞數(shù)據(jù)。
Action:
export const ADD = 'ADD'
export const add = () => ({
? ?type: ADD
})
export const SUB = 'SUB'
export const sub = () => ({
? ?type: SUB
})
什么是Reducer?
設(shè)計(jì)state結(jié)構(gòu)
Action處理
reducer是一個純函數(shù),接受舊的state和action,返回新的state。
注意: 每個 reducer 只負(fù)責(zé)管理全局 state 中它負(fù)責(zé)的一部分。每個 reducer 的 state 參數(shù)都不同,分別對應(yīng)它管理的那部分 state 數(shù)據(jù)。
Reducer:
import { ADD, SUB } from 'Action'
const counter = (state = 0, action) => {
? ?switch (action.type) {
? ? ? ?case ADD:
return state + 1
case SUB:
return state - 1
default:
return state
}
}
總結(jié):把要做的修改變成一個普通對象,這個對象叫做action,而不是直接修改state。然后編寫專門的函數(shù)來決定每個action如何改變應(yīng)用的state,這個函數(shù)叫做reducer。
二、關(guān)于Redux的重點(diǎn)知識
1. 三大原則
(1)單一數(shù)據(jù)源
整個應(yīng)用的state被存儲在一棵object tree中,并且這個object tree只存在于唯一一個store中。
(2)state是只讀的
唯一改變state的方法就是觸發(fā)action,action是一個用于描述已發(fā)生事件的普通對象。
(3)使用純函數(shù)來執(zhí)行修改
為了描述action如何改變state,需要寫reducers。
注意:永遠(yuǎn)不要在reducer中做這些操作
修改傳入?yún)?shù);
執(zhí)行有副作用的操作,如API請求和路由跳轉(zhuǎn);
調(diào)用非純函數(shù),如Date.now(), Math.random()。
只要傳入?yún)?shù)相同,返回計(jì)算得到的下一個state就一定相同。沒有特殊情況,沒有副作用,沒有API請求,沒有變量修改,單純執(zhí)行計(jì)算。
2.combineReducer
為什么使用combineReducer?
隨著應(yīng)用變得復(fù)雜,需要對 reducer 函數(shù)進(jìn)行拆分,拆分后的每一塊獨(dú)立負(fù)責(zé)管理 state 的一部分。
combineReducers 輔助函數(shù)的作用是,把一個由多個不同 reducer 函數(shù)作為 value 的 object,合并成一個最終的 reducer 函數(shù),然后就可以對這個 reducer 調(diào)用 createStore。
合并后的 reducer 可以調(diào)用各個子 reducer,并把它們的結(jié)果合并成一個 state 對象。state 對象的結(jié)構(gòu)由傳入的多個 reducer 的 key 決定。
import { combineReducer } from 'redux'
const todoApp = combineReducer({
? ?visibilityFilter,
? ?todos
})
export default todoApp
等價寫法 =>
export const todoApp = (state = {}, action) => {
? ?return {
? ? ? ?visibilityFilter: visibilityFilter(state.visibilityFilter, action)
? ? ? ?todos: todos(state.todos, action)
? ?}
}
conbineReducer所做的是生成一個函數(shù),這個函數(shù)來調(diào)用你的一系列reducer,每個reducer根據(jù)他的key來篩選出state中的一部分?jǐn)?shù)據(jù)并處理,然后這個生成的函數(shù)再將所有reducer的結(jié)果合并成一個大的對象。
combineReducer示例:
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)
3.Redux中的connect()方法
(1)定義mapStateToProps方法
把當(dāng)前Redux store state映射到展示組件的props中。
(2)定義mapDispatchToProps方法
接收dispatch()方法并返回期望注入到展示組件的props中的回調(diào)方法。
import { connect } from 'react-redux'
import { Task } from './reducers'
const mapStateToProps = (state) => {
? ?return {
? ? ? ?tasks: state.entities.tasks
? ?}
}
const mapDispatchToProps = (dispatch) => {
? ?return {
? ? ? ?addUnit: (taskID, unitID, units) => {
? ? ? ? ? ?dispatch(Task.addUnit(taskID, unitID, units))
? ? ? ?}
? ?}
}
等價于 =>
const mapDispatchToProps = (dispatch) => {
? ?return {
? ? ? ?addUnit: (taskID, unitID, units) => Task.addUnit(taskID, unitID, units))
? ?}
}
//多個conncet的書寫方式
const VisibleCreateUnit = connect(
? ?mapStateToProps,
? ?mapDispatchToProps
)(CreateUnit)
const VisibleCreateTask = connect(
? ?mapStateToProps,
? ?mapDispatchToProps
)(CreateTask)
export { VisibleCreateUnit, VisibleCreateTask }
4.Store
(1)createStore()
createStore() 的第二個參數(shù)是可選的, 用于設(shè)置 state 初始狀態(tài)。這對開發(fā)同構(gòu)應(yīng)用時非常有用,服務(wù)器端 redux 應(yīng)用的 state 結(jié)構(gòu)可以與客戶端保持一致, 那么客戶端可以將從網(wǎng)絡(luò)接收到的服務(wù)端 state 直接用于本地?cái)?shù)據(jù)初始化。
(2)Redux Provider
import { Provider } from 'react-redux'
import { createStore } from 'redux'
在React-router中使用Provider
const store = createStore(appReducer, normalizedState)
5.數(shù)據(jù)序列化(normalize)
示例:Tasks中包含subTasks和units,subTasks中又包含units。
import { normalize, schema } from 'normalizr'
//首先使用schema最小單元即units
const unitsSchema = new schema.Entity('units')
//subTasks中包含units
const subTasksSchema = new schema.Entity('subTasks', { units: [unitsSchema] })
//tasks中包含subTasks和units
const tasksSchema = new schema.Entity('tasks', {
subTasks: [subTasksSchema],
units: [unitsSchema]
})
......
const initialState = {
? ?tasks: {
? ? ? ?subTasks: {
? ? ? ? ? ?units: {
? ? ? ? ? ? ? ?......
? ? ? ? ? ?}
? ? ? ? ? ?......
? ? ? ?},
? ? ? ?units: {
? ? ? ? ? ?......
? ? ? ?}
? ?},
? ?groups: {
? ? ? ?......
? ?},
? ?classes: {
? ? ? ?......
? ?},
? ?rosters: {
? ? ? ?......
? ?}
}
const stateSchema = {
? ?tasks: [tasksSchema],
? ?groups: [groupsSchema],
? ?classes: [classesSchema],
? ?rosters: [rostersSchema]
}
const normalizedState = normalize(initialState, stateSchema)
console.log('normalizedState', normalizedState)
Object{
? ?entities: Object{
? ? ? ?.....
? ?}
? ?result: Object{
? ? ? ?tasks: array[2],
? ? ? ?groups: array[3],
? ? ? ?classes: array[4],
? ? ? ?rosters: array[5]
? ?}
}
注意:鍵一定與數(shù)組中的鍵一一對應(yīng)