重用 Redux 中的 reducer
一、使用 Multireducer 重用相同的 reducer
在使用 Redux 開發應用的過程中,你可能會發現自己經常需要複製黏貼一些重複的reducer。舉個例子,你可能需要實現一個東西的不同列表, 比如已發布的文章列表和未發布的文章列表,你會怎麼實現?相比於實現兩個叫 publishedPosts 和 draftPosts 的 reducer 跟對應的叫 fetchPublishedPosts() 和 fetchDraftPosts() 的 action creator,如果只用一個 posts reducer, 程序會更簡單,維護也會更容易。
但是,你沒辦法在 Redux 中這樣做:
import posts from ./reducers/posts;nnconst reducer = combineReducers({n published: posts, // 錯n draft: posts, // 錯n});n
這樣的話每個 reduer 都會處理同樣的 action。
而 Multireducer 可以讓把同一個 reducer 掛載到不同狀態樹節點,而且自動處理對應的 action。下面我們來看怎麼用 multireducer 來處理上面文章的例子。
安裝$ npm i multireducer --saven
配置reducer
import multireducer from multireducer;nimport posts from ./reducers/posts;nnconst reducer = combineReducers({n posts: multireducer({n published: posts,n draft : posts,n })n});n
調整相關的組件
import React, { Component, PropTypes } from react;nimport { connect } from react-redux;n// 注意這裡用的是 multireducer 的 bindActionCreatorsnimport { bindActionCreators } from multireducer;nimport { fetch } from ./actions/posts;nnclass PostList extends Component {n static propTypes = {n posts: PropTypes.array.isRequired,n fetch: PropTypes.func.isRequired,n }nn componentWillMount() {n this.props.fetch()n }nn render() {n const { posts } = this.props;n return (n <div>n <ul>n {posts.map(post =>n <li key={post.id}>n {post.title}n </li>n )}n </ul>n </div>n );n }n}nnPostList = connect(n (state, { as }) => ({ posts: state.posts[as] }),n (dispatch, { as }) => bindActionCreators({ fetch }, dispatch, as)n)(PostList);nnexport default PostList;n
然後我們可以這樣用這個 PostList:
<div>n <PostList as="published"/>n <PostList as="draft"/>n</div>n
二、使用高階函數抽象 reducer
上面介紹的 multireducer 適用於同一個東西的不同邏輯,還有一種情況就是不同的東西會有相同的邏輯,比如我們有一個文章列表和一個用戶列表,它們的 reducer 是這樣的:
// reducers/products.jsnconst reducer = (state, { type, payload }) => {n switch (type) {n case products/FETCH_SUCCESS:n return {n ...state,n loading: false,n list: payloadn }n default:n return staten }n}n
// reducers/users.jsnconst reducer = (state, { type, payload }) => {n switch (type) {n case users/FETCH_SUCCESS:n return {n ...state,n loading: false,n list: payloadn }n default:n return staten }n}n
這裡兩個 reducer 幾乎是一樣的,我們可以寫一個叫 list 的 reducer 來把兩個 reducer 相同的部分提取出來:
const list = (actionType) => {n return (state, { type, payload }) => {n switch (type) {n case actionType:n return {n ...state,n loading: false,n list: payloadn }n break;n default:n return staten }n }n}n
然後我們可以這樣用這個 reducer:
// reducers/products.jsnconst reducer = (state, action) => {n switch (action.type) {n // 其他邏輯n default:n return staten }n}nnconst finnalReducer = (state, action) => {n state = list(products/FETCH_SUCCESS)(state, action)n return reducer(state, action)n}n
// reducers/users.jsnconst reducer = (state, action) => {n switch (action.type) {n // 其他邏輯n default:n return staten }n}nnconst finnalReducer = (state, action) => {n state = list(users/FETCH_SUCCESS)(state, action)n return reducer(state, action)n}n
這樣 list 的邏輯就被抽象出來了,但是隨著抽象的東西越來越多,你會發現 reducer 會變成這樣:
// reducers/products.jsnconst reducer = (state, action) => {n switch (type) {n // 其他邏輯n default:n return staten }n}nnconst finnalReducer = (state, action) => {n state = list(products/FETCH_SUCCESS)(state, action)n state = query(products/FETCH_SUCCESS)(state, action)n state = pagination(products/FETCH_SUCCESS)(state, action)n return reducer(state, action)n}n
這些抽象 reducer 的調用過程都很類似,有沒有更優雅的辦法來組合這些抽象的 reducer 呢?
我們來實現一個 composeReducers 組合這些 reducer:
function composeReducers(...reducers) {n return (state, action) => {n if (reducers.length === 0) {n return staten }nn const last = reducers[reducers.length - 1]n const rest = reducers.slice(0, -1)nn return rest.reduceRight((enhanced, reducer) => reducer(enhanced, action), last(state, action))n }n}n
利用這個 composeReducers 我們就可以這樣組合 reducer 了:
// reducers/products.jsnconst reducer = (state, action) => {n switch (type) {n // 其他邏輯n default:n return staten }n}nnconst finnalReducer = composeReducers(n reducer(state, action),n list(products/FETCH_SUCCESS),n query(products/FETCH_SUCCESS),n pagination(products/FETCH_SUCCESS)n)n
至此,我們通過 multireducer 和 composeReducers 實現了 reducer 中相同邏輯的抽象和重用。
推薦閱讀:
※React 從青銅到王者系列教程之倔強青銅篇
※React 許可證雖嚴苛,但不必過度 react
※基於Decorator的組件擴展實踐
※[上海] 招前端,移動端,小程序開發(多圖)
※React 16 中的異常處理