一通惫、認(rèn)識(shí)Redux
安裝
npm install --save redux?
npm install --save react-redux
一句話理解Redux
應(yīng)用中的所有state都以一個(gè)對象數(shù)樹的方式存儲(chǔ)在一個(gè)單一的store中搀军,唯一改變state的方法就是觸發(fā)action强缘,一個(gè)描述發(fā)生什么的對象片习,為了描述action如何改變state樹延届,需要編寫reducers址貌。
什么是Action脚翘?
Action本質(zhì)上是JavaScript的普通對象骂因,action內(nèi)必須使用一個(gè)字符串類型的type字段來表示將要執(zhí)行的動(dòng)作炎咖。多數(shù)情況下type會(huì)被定義成字符串常量。我們應(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是一個(gè)純函數(shù),接受舊的state和action俄烁,返回新的state绸栅。
注意: 每個(gè) reducer 只負(fù)責(zé)管理全局 state 中它負(fù)責(zé)的一部分。每個(gè) 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é):把要做的修改變成一個(gè)普通對象蓖柔,這個(gè)對象叫做action,而不是直接修改state风纠。然后編寫專門的函數(shù)來決定每個(gè)action如何改變應(yīng)用的state况鸣,這個(gè)函數(shù)叫做reducer。
二竹观、關(guān)于Redux的重點(diǎn)知識(shí)
1. 三大原則
(1)單一數(shù)據(jù)源
整個(gè)應(yīng)用的state被存儲(chǔ)在一棵object tree中镐捧,并且這個(gè)object tree只存在于唯一一個(gè)store中。
(2)state是只讀的
唯一改變state的方法就是觸發(fā)action臭增,action是一個(gè)用于描述已發(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ì)算得到的下一個(gè)state就一定相同并炮。沒有特殊情況默刚,沒有副作用,沒有API請求逃魄,沒有變量修改荤西,單純執(zhí)行計(jì)算。
2.combineReducer
為什么使用combineReducer?
隨著應(yīng)用變得復(fù)雜伍俘,需要對 reducer 函數(shù)進(jìn)行拆分邪锌,拆分后的每一塊獨(dú)立負(fù)責(zé)管理 state 的一部分。
combineReducers 輔助函數(shù)的作用是癌瘾,把一個(gè)由多個(gè)不同 reducer 函數(shù)作為 value 的 object觅丰,合并成一個(gè)最終的 reducer 函數(shù),然后就可以對這個(gè) reducer 調(diào)用 createStore妨退。
合并后的 reducer 可以調(diào)用各個(gè)子 reducer妇萄,并把它們的結(jié)果合并成一個(gè) state 對象。state 對象的結(jié)構(gòu)由傳入的多個(gè) reducer 的 key 決定咬荷。
import { combineReducer } from 'redux'
const todoApp = combineReducer({
? ?visibilityFilter,
? ?todos
})
export default todoApp
等價(jià)寫法 =>
export const todoApp = (state = {}, action) => {
? ?return {
? ? ? ?visibilityFilter: visibilityFilter(state.visibilityFilter, action)
? ? ? ?todos: todos(state.todos, action)
? ?}
}
conbineReducer所做的是生成一個(gè)函數(shù)冠句,這個(gè)函數(shù)來調(diào)用你的一系列reducer,每個(gè)reducer根據(jù)他的key來篩選出state中的一部分?jǐn)?shù)據(jù)并處理,然后這個(gè)生成的函數(shù)再將所有reducer的結(jié)果合并成一個(gè)大的對象幸乒。
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))
? ? ? ?}
? ?}
}
等價(jià)于 =>
const mapDispatchToProps = (dispatch) => {
? ?return {
? ? ? ?addUnit: (taskID, unitID, units) => Task.addUnit(taskID, unitID, units))
? ?}
}
//多個(gè)conncet的書寫方式
const VisibleCreateUnit = connect(
? ?mapStateToProps,
? ?mapDispatchToProps
)(CreateUnit)
const VisibleCreateTask = connect(
? ?mapStateToProps,
? ?mapDispatchToProps
)(CreateTask)
export { VisibleCreateUnit, VisibleCreateTask }
4.Store
(1)createStore()
createStore() 的第二個(gè)參數(shù)是可選的, 用于設(shè)置 state 初始狀態(tài)罕扎。這對開發(fā)同構(gòu)應(yīng)用時(shí)非常有用聚唐,服務(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)