核心思路
將變化的動作和數(shù)據(jù)本身抽象并出來并分離開。提供統(tǒng)一的API發(fā)起變化和更新數(shù)據(jù)
Redux中組成部分
action + reducer + store
action
Action 是把數(shù)據(jù)從應(yīng)用(譯者注:這里之所以不叫 view 是因為這些數(shù)據(jù)有可能是服務(wù)器響應(yīng),用戶輸入或其它非 view 的數(shù)據(jù) )傳到 store 的有效載荷。它是 store 數(shù)據(jù)的唯一來源。
https://www.redux.org.cn/docs/basics/Actions.html
Reducer
Reducers 指定了應(yīng)用狀態(tài)的變化如何響應(yīng) actions 并發(fā)送到 store 的,記住 actions 只是描述了有事情發(fā)生了這一事實,并沒有描述應(yīng)用如何更新 state。
拆分reducer
拆分前:
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
case TOGGLE_TODO:
return Object.assign({}, state, {
todos: state.todos.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
})
default:
return state
}
}
拆分后
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
default:
return state
}
}
function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
使用combineReducers拆分reducer
import { combineReducers } from 'redux'
import {
ADD_TODO,
TOGGLE_TODO,
SET_VISIBILITY_FILTER,
VisibilityFilters
} from './actions'
const { SHOW_ALL } = VisibilityFilters
function visibilityFilter(state = SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return action.filter
default:
return state
}
}
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
]
case TOGGLE_TODO:
return state.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
default:
return state
}
}
const todoApp = combineReducers({
visibilityFilter,
todos
})
export default todoApp
Store
就是把它們聯(lián)系到一起的對象。Store 有以下職責(zé):
- 維持應(yīng)用的 state;
- 提供
getState()
方法獲取 state; - 提供
dispatch(action)
方法更新 state; - 通過
subscribe(listener)
注冊監(jiān)聽器; - 通過
subscribe(listener)
返回的函數(shù)注銷監(jiān)聽器。
三大原則
1.單一數(shù)據(jù)源.整個應(yīng)用的 state 被儲存在一棵 object tree 中,并且這個 object tree 只存在于唯一一個 [store]
2.State 是只讀的.唯一改變 state 的方法就是觸發(fā) action,action 是一個用于描述已發(fā)生事件的普通對象
3.使用純函數(shù)來執(zhí)行修改.為了描述 action 如何改變 state tree ,你需要編寫 reducers。
Redux和Flux的對比
不同于 Flux ,Redux 并沒有 dispatcher 的概念。原因是它依賴純函數(shù)來替代事件處理器。純函數(shù)構(gòu)建簡單,也不需額外的實體來管理它們。
數(shù)據(jù)流
嚴(yán)格的單向數(shù)據(jù)流是 Redux 架構(gòu)的設(shè)計核心。
1.調(diào)用 store.dispatch(action)
。
2.Redux store 調(diào)用傳入的 reducer 函數(shù)。
3.根 reducer 應(yīng)該把多個子 reducer 輸出合并成一個單一的 state 樹。
4.Redux store 保存了根 reducer 返回的完整 state 樹。
異步action
保持reducer的純凈,把異步邏輯放到action中
const fetchPosts = subreddit => dispatch => {
dispatch(requestPosts(subreddit))
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(subreddit, json)))
}
middleware
middleware提供的是位于 action 被發(fā)起之后,到達 reducer 之前的擴展點。 你可以利用 Redux middleware 來進行日志記錄、創(chuàng)建崩潰報告、調(diào)用異步接口或者路由等等。
因為所有的操作都被統(tǒng)一收口再redux中,所以可以很方便簡潔地插入middleware
在reducer中插入middleware
const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
middleware.push(createLogger())
}
const store = createStore(
reducer,
applyMiddleware(...middleware)
)
日志記錄和上報
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
import { createStore, combineReducers, applyMiddleware } from 'redux'
let todoApp = combineReducers(reducers)
let store = createStore(
todoApp,
// applyMiddleware() 告訴 createStore() 如何處理中間件
applyMiddleware(logger, crashReporter)
)