(1) redux簡(jiǎn)介

reducer
reducer 是一個(gè)計(jì)劃函數(shù),接收舊的 state 和 action,生成新的 state
? ? ? ? ? ? combineReducers:多 reducer 合并成一個(gè) reducer
? ? ? ? ? ? state:狀態(tài)
? ? ? ? ? ? action:一個(gè)對(duì)象,必須包含 type 字段
store
存儲(chǔ)器,存儲(chǔ)所有的狀態(tài)和方法(state狀態(tài) ,subscribe訂閱方法,dispatch狀態(tài)修改方法)
? ? ? ? ? createStore:創(chuàng)建 store 對(duì)象,包含 getState, dispatch, subscribe等
? ? ? ? ? dispatch:觸發(fā) action,生成新的 state
? ? ? ? ? subscribe:實(shí)現(xiàn)訂閱功能,每次觸發(fā) dispatch 的時(shí)候,會(huì)執(zhí)行訂閱函數(shù)
view
視圖層,控制顯示
middleware
擴(kuò)展 dispatch 函數(shù),dispatch 函數(shù)中不同功能 的方法提取出來(lái)
(2) ?Redux 的工作流程

1)創(chuàng)建Store
import { createStore } from 'redux';
const defaultState = 0;
// Action 就是 View 發(fā)出的通知,表示 State 應(yīng)該要發(fā)生變化了。其中的type屬性是必須的,表示 Action 的名稱。其他屬性可以自由設(shè)置
const action = {
? type: 'ADD',
? payload: 2
};
// Reducer 是一個(gè)函數(shù),它接受 Action 和當(dāng)前 State 作為參數(shù),返回一個(gè)新的 State。Store 收到 Action 以后,必須給出一個(gè)新的 State,這樣 View 才會(huì)發(fā)生變化。
const reducer = (state = defaultState, action) => {
? switch (action.type) {
? ? case 'ADD':
? ? ? return state + action.payload;
? ? default:
? ? ? return state;
? }
};
// createStore接受 Reducer 作為參數(shù),生成一個(gè)新的 Store
const store = createStore(reducer);
2) 用戶發(fā)出 Action
// Action 是一個(gè)對(duì)象。其中的type屬性是必須的,表示 Action 的名稱。其他屬性可以自由設(shè)置
// View 要發(fā)送多少種消息,就會(huì)有多少種 Action。如果都手寫(xiě),會(huì)很麻煩??梢远x一個(gè)函數(shù)來(lái)生成 Action,這個(gè)函數(shù)就叫 Action Creator,如addTodo就是Action Creator
function addTodo(text) {
? return {
? ? type: 'ADD',
? ? text
? }
}
const action = addTodo(3);
// store.dispatch()是 View 發(fā)出 Action 的唯一方法,會(huì)觸發(fā) Reducer 的自動(dòng)執(zhí)行
store.dispatch(action);
3) Store 允許使用store.subscribe方法設(shè)置監(jiān)聽(tīng)函數(shù),一旦 State 發(fā)生變化,就自動(dòng)執(zhí)行這個(gè)函數(shù)。
//? listener可以通過(guò)store.getState()得到當(dāng)前狀態(tài)。只要把 View 的更新函數(shù)(對(duì)于 React 項(xiàng)目,就是組件的render方法或setState方法)放入listen,就會(huì)實(shí)現(xiàn) View 的自動(dòng)渲染。
function listerner() {
? let newState = store.getState();
? component.setState(newState);
}
store.subscribe(listener);
4) Redux 提供了一個(gè)combineReducers方法,用于 Reducer 的拆分。你只要定義各個(gè)子 Reducer 函數(shù),然后用這個(gè)方法,將它們合成一個(gè)大的 Reducer。
import { combineReducers } from 'redux';
const chatReducer = combineReducers({
? chatLog,
? statusMessage,
? userName
})
export default chatReducer;
或者可以把所有子 Reducer 放在一個(gè)文件里面,然后統(tǒng)一引入。
import { combineReducers } from 'redux'
i
import * as reducers from './reducers'
const reducer = combineReducers(reducers)
5) 中間件是一個(gè)函數(shù),對(duì)store.dispatch方法進(jìn)行了改造,在發(fā)出 Action 和執(zhí)行 Reducer 這兩步之間,添加了其他功能。
createStore方法可以接受整個(gè)應(yīng)用的初始狀態(tài)作為參數(shù),那樣的話,applyMiddleware就是第三個(gè)參數(shù)了。
// applyMiddlewares是 Redux 的原生方法,作用是將所有中間件組成一個(gè)數(shù)組,依次執(zhí)行。
const store = createStore(
? reducer,
? initial_state,
? applyMiddleware(thunk, promise, logger)
);
常用中間件:
redux-thunk 中間件
異步操作的第一種解決方案就是,寫(xiě)出一個(gè)返回函數(shù)的 Action Creator,然后使用redux-thunk中間件改造store.dispatch
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
// 使用redux-thunk中間件,改造store.dispatch,使得后者可以接受函數(shù)作為參數(shù)。
const store = createStore(
? reducer,
? applyMiddleware(thunk)
);
// fetchPosts是一個(gè)Action Creator(動(dòng)作生成器),返回一個(gè)函數(shù)。這個(gè)函數(shù)執(zhí)行后,先發(fā)出一個(gè)Action(requestPosts(postTitle)),然后進(jìn)行異步操作。拿到結(jié)果后,先將結(jié)果轉(zhuǎn)成 JSON 格式,然后再發(fā)出一個(gè) Action(?receivePosts(postTitle, json))。
const fetchPosts = postTitle => (dispatch, getState) => {
? dispatch(requestPosts(postTitle));
? return fetch(`/some/API/${postTitle}.json`)
? ? .then(response => response.json())
? ? .then(json => dispatch(receivePosts(postTitle, json)));
? };
};
store.dispatch(fetchPosts('reactjs'));
redux-promise 中間件
另一種異步操作的解決方案,就是讓 Action Creator 返回一個(gè) Promise 對(duì)象。
import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';
import reducer from './reducers';
//promiseMiddleware中間件使得store.dispatch方法可以接受 Promise 對(duì)象作為參數(shù)。
const store = createStore(
? reducer,
? applyMiddleware(promiseMiddleware)
);
//Action Creator 有兩種寫(xiě)法。寫(xiě)法一,返回值是一個(gè) Promise 對(duì)象。
const fetchPosts =
? (dispatch, postTitle) => new Promise(function (resolve, reject) {
? ? dispatch(requestPosts(postTitle));
? ? return fetch(`/some/API/${postTitle}.json`)
? ? ? .then(response => {
? ? ? ? type: 'FETCH_POSTS',
? ? ? ? payload: response.json()
? ? ? });
});
//寫(xiě)法二, Action 對(duì)象的payload屬性是一個(gè) Promise 對(duì)象。這需要從redux-actions模塊引入createAction方法,并且寫(xiě)法也要變成下面這樣。
import { createAction } from 'redux-actions';
const fetchPosts = createAction(
? ? ? 'FETCH_POSTS',
? ? ? fetch(`/some/API/${postTitle}.json`)
? ? ? ? .then(response => response.json())
? ? )
(3) React-Redux
1)UI組件與容器組件
UI 組件負(fù)責(zé) UI 的呈現(xiàn),容器組件負(fù)責(zé)管理數(shù)據(jù)和邏輯。
UI 組件:
只負(fù)責(zé) UI 的呈現(xiàn),不帶有任何業(yè)務(wù)邏輯
沒(méi)有狀態(tài)(即不使用this.state這個(gè)變量)
所有數(shù)據(jù)都由參數(shù)(this.props)提供
不使用任何 Redux 的 API
容器組件:
負(fù)責(zé)管理數(shù)據(jù)和業(yè)務(wù)邏輯,不負(fù)責(zé) UI 的呈現(xiàn)
帶有內(nèi)部狀態(tài)
使用 Redux 的 API
React-Redux 規(guī)定,所有的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動(dòng)生成。
2)connect()
React-Redux 提供connect方法,用于從 UI 組件生成容器組件。connect的意思,就是將這兩種組件連起來(lái)。connect方法的完整 API 如下:
import { connect } from 'react-redux'
const VisibleTodoList = connect(
? mapStateToProps,
? mapDispatchToProps
)(TodoList)
上面代碼中,connect方法接受兩個(gè)參數(shù):mapStateToProps和mapDispatchToProps。它們定義了 UI 組件的業(yè)務(wù)邏輯。前者負(fù)責(zé)輸入邏輯,即將state映射到 UI 組件的參數(shù)(props),后者負(fù)責(zé)輸出邏輯,即將用戶對(duì) UI 組件的操作映射成 Action。
mapStateToProps()
mapStateToProps是一個(gè)函數(shù)。它的作用就是像它的名字那樣,建立一個(gè)從(外部的)state對(duì)象到(UI 組件的)props對(duì)象的映射關(guān)系。
// mapStateToProps是一個(gè)函數(shù),它接受state作為參數(shù),返回一個(gè)對(duì)象。這個(gè)對(duì)象有todos屬性和visibilityFilter屬性,代表 UI 組件的同名參數(shù)
const mapStateToProps = (state) => {
? return {
? ? todos: getVisibleTodos(state.todos, state.visibilityFilter)
? }
}
// mapStateToProps的第一個(gè)參數(shù)總是state對(duì)象,還可以使用第二個(gè)參數(shù),代表容器組件的props對(duì)象。
const mapStateToProps = (state, ownProps) => {
? return {
? ? active: ownProps.filter === state.visibilityFilter
? }
}
// getVisibleTodos從state算出?todos?的值
const getVisibleTodos = (todos, filter) => {
? switch (filter) {
? ? case 'SHOW_ALL':
? ? ? return todos
? ? case 'SHOW_COMPLETED':
? ? ? return todos.filter(t => t.completed)
? ? case 'SHOW_ACTIVE':
? ? ? return todos.filter(t => !t.completed)
? ? default:
? ? ? throw new Error('Unknown filter: ' + filter)
? }
}
mapDispatchToProps()
mapDispatchToProps是connect函數(shù)的第二個(gè)參數(shù),用來(lái)建立 UI 組件的參數(shù)到store.dispatch方法的映射。也就是說(shuō),它定義了哪些用戶的操作應(yīng)該當(dāng)作 Action,傳給 Store。它可以是一個(gè)函數(shù),也可以是一個(gè)對(duì)象。
// 如果mapDispatchToProps是一個(gè)函數(shù),會(huì)得到dispatch和ownProps(容器組件的props對(duì)象)兩個(gè)參數(shù)。mapDispatchToProps作為函數(shù),應(yīng)該返回一個(gè)對(duì)象,該對(duì)象的每個(gè)鍵值對(duì)都是一個(gè)映射,定義了 UI 組件的參數(shù)怎樣發(fā)出 Action。
const mapDispatchToProps = (
? dispatch,
? ownProps
) => {
? return {
? ? onClick: () => {
? ? ? dispatch({
? ? ? ? type: 'SET_VISIBILITY_FILTER',
? ? ? ? filter: ownProps.filter
? ? ? });
? ? }
? };
}
// 如果mapDispatchToProps是一個(gè)對(duì)象,它的每個(gè)鍵名也是對(duì)應(yīng) UI 組件的同名參數(shù),鍵值應(yīng)該是一個(gè)函數(shù),會(huì)被當(dāng)作 Action creator ,返回的 Action 會(huì)由 Redux 自動(dòng)發(fā)出。
const mapDispatchToProps = {
? onClick: (filter) => {
? ? type: 'SET_VISIBILITY_FILTER',
? ? filter: filter
? };
}
3)<Provider> 組件
React-Redux 提供Provider組件,可以讓容器組件拿到state。
import { Provider } from 'react-redux'
i
import { createStore } from 'redux'
i
import todoApp from './reducers'
i
import App from './components/App'
let store = createStore(todoApp);
// Provider在根組件外面包了一層,這樣一來(lái),App的所有子組件就默認(rèn)都可以拿到state了。
render(
? <Provider store={store}>
? ? <App />
? </Provider>,
? document.getElementById('root')
)
// store放在了上下文對(duì)象context上面。調(diào)用時(shí),子組件可以從context拿到store
class VisibleTodoList extends Component {
? componentDidMount() {
? ? const { store } = this.context;
? ? this.unsubscribe = store.subscribe(() =>
? ? ? this.forceUpdate()
? ? );
? }
? render() {
? ? const props = this.props;
? ? const { store } = this.context;
? ? const state = store.getState();
? ? // ...
? }
}
VisibleTodoList.contextTypes = {
? store: React.PropTypes.object
}