如何搭建
安装
首先我们先执行这条语句来安装我们需要的插件:
npm i redux react-redux redux-saga reduxsauce seamless-immutable --save
Redux DevTools
具体的安装方法在这里
文件目录
root
├── App.js
├── config.js
├── index.jsx
├── reducers
│ ├── detail.js
│ ├── home.js
│ └── index.js
└── saga
├── detail.js
├── home.js
└── index.js
Redux+saga
/*
* 入口文件index.jsx
*/
import React from 'react'
import { render } from 'react-dom'
import { HashRouter as Router, withRouter } from 'react-router-dom'
import App from './App'
import { createStore, applyMiddleware, compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import rootSaga from './sagas'
import { Provider } from 'react-redux'
// 如果要使用redux devtools,必须添加这句代码
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
composeEnhancers(applyMiddleware(sagaMiddleware))
)
sagaMiddleware.run(rootSaga)
const AppWithRouter = withRouter(App) // withRouter 的作用是将history,location,match 三个对象传给子组件
render(
,
document.getElementById('app')
)
Reducers
为了便于管理,我们将所有的reducers用combineReducers来合并:
/*
* reducers/index.js
*/
import { combineReducers } from 'redux'
import detail from './detail'
import home from './home'
const rootReducer = combineReducers({
detail,
home
})
export default rootReducer
每一个reducer的具体实现是这样的(举例),同时也给出了reduxsauce的用法,此处用于生成ActionTypes和reducers,其实这里已经给出了一种better practice,reducer只负责修改数据,而不负责具体业务,所以只需要提供几个增删改的功能就可以了:
import Immutable from 'seamless-immutable'
import {
Types as ReduxSauceTypes,
createReducer,
createTypes
} from 'reduxsauce'
export const types = createTypes(
`
MERGE_DATA
SET_IN
REMOVE
RESET
`,
{ prefix: 'DETAIL_' }
)
const INITIAL_STATE = Immutable({
supplierID: null,
recruitID: null,
userType: null, // supplier(供应商), shop(商户)
data: {},
list: {},
step: 0,
mode: 'common', // modify, common
tabs: {
defaultActiveKey: 'info',
activeKey: 'info'
},
transfer: {
targets: {}
},
modal: {
type: '',
title: '',
visible: false
}
})
export const mergeData = (state = INITIAL_STATE, action) => {
const { payload } = action
return state.setIn(['data', payload.status], payload.data)
}
export const setIn = (state = INITIAL_STATE, action) => {
const { payload } = action
const { path, value } = payload
return state.setIn(path, value)
}
export const remove = (state = INITIAL_STATE, action) => {
const { payload } = action
const { path, status } = payload
const prev = state.getIn(path)
let next = prev
if (Array.isArray(prev)) {
let arr = prev.asMutable()
arr.splice(status, 1)
next = arr
} else {
next = prev.without(status)
}
return state.setIn(path, next)
}
export const reset = (state = INITIAL_STATE, action) => {
return INITIAL_STATE.set('supplierID', state.supplierID)
.set('recruitID', state.recruitID)
.set('userType', state.userType)
}
export const HANDLERS = {
[ReduxSauceTypes.DEFAULT]: defaultHandler,
[types.MERGE_DATA]: mergeData,
[types.SET_IN]: setIn,
[types.REMOVE]: remove,
[types.RESET]: reset
}
export default createReducer(INITIAL_STATE, HANDLERS)
saga
其实index.jsx接受的saga是一个generator函数,这个generator函数,最原生的写法是:
import { delay } from 'redux-saga'
import { call, put, takeEvery } from 'redux-saga/effects'
export function* helloSaga() {
console.log('Hello Saga!')
}
export function* incrementAsync() {
yield call(delay, 1000)
yield put({ type: 'INCREMENT' })
}
export function* watchIncrementAsync() {
yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}
// single entry point to start all Sagas at once
export default function* rootSaga() {
yield [helloSaga(), watchIncrementAsync()]
}
我这里做了一下变形,也就是把几个文件的saga合并起来:
import detailSaga from './detail'
import * as homeSaga from './home'
const genRoot = sagas => {
let sagaArr = []
Object.keys(sagas).forEach(sagasKey => {
let item = sagas[sagasKey]
Object.keys(item).forEach(key => {
sagaArr.push(item[key]())
})
})
return sagaArr
}
export default function * rootSaga () {
yield genRoot({ detailSaga, homeSaga })
}
至此,项目的搭建方法大概就是这样,但是仅仅是会用还是不够的,刚开始使用这些工具的时候,我也踩了很多坑,徒增了很多代码量,后来经过优化以后,减少了很多代码,下面是我优化的过程。