项目结构
-
public
-
src
-
store
redux仓库目录
-
actionCreators.js
action目录
-
actionTypes.js
action type目录
-
index.js
redux入口文件,生成store
-
reducer.js
redux逻辑文件,编写各个action所对应的逻辑
-
sagas.js
saga逻辑文件,根据type捕捉相应action进行中间处理
-
-
index.js
react项目入口文件
-
TodoList.js
组件
-
TodoListUI.js
redux逻辑文件,编写各个action所对应的逻辑
-
-
package.json
-
README.md
可见项目结构随着redux-saga中间件的添加,并没有太多的变化。
其中主要发生改变的有store/index.js,以及新添加的store/sagas.js文件。
TodoListUI.js为为了将逻辑分离而单提出来的组件。
结构更改
TodoList的逻辑分离
TodoListUI
import React from 'react';
const TodoListUI = (props) => {
const {inputValue, list, handleInputChange, handleButtonClick, handleLiClick} = props
return (
<div>
<div>
<input value={inputValue} onChange={handleInputChange}></input>
<button onClick={handleButtonClick}>确定</button>
</div>
<ul>
{list.map((item, index)=> (
<li key={index} onClick={()=>handleLiClick(index)}>{item}</li>
))}
</ul>
</div>
)
}
export default TodoListUI
TodoListUI为一个无状态组件,接收一个参数并render一个组件。
TodoList
import React, { Component } from 'react';
import TodoListUI from './TodoListUI';
import { connect } from 'react-redux'
import { buttonClickAction, didMountAction, inputChangeAction, itemClickAction } from './store/actionCreators';
class TodoList extends Component {
componentDidMount() {
this.props.didMount()
}
render() {
const { inputValue, list, handleInputChange, handleButtonClick, handleLiClick } = this.props
return (
<TodoListUI
inputValue={inputValue}
list={list}
handleInputChange={handleInputChange}
handleButtonClick={handleButtonClick}
handleLiClick={handleLiClick}
/>
);
}
}
const stateToProps = (state) => {
return {
inputValue: state.inputValue,
list: state.valueList
}
}
const dispatchToProps = (dispatch) => {
return {
handleInputChange(e) {
//dispatch(action)
},
handleButtonClick() {
//dispatch(action)
},
handleLiClick(index) {
//dispatch(action)
},
didMount(){
const action = didMountAction()
dispatch(action)
}
}
}
export default connect(stateToProps, dispatchToProps)(TodoList)
由代码可见TodoList相比于我们上次react-redux中的TodoList并没有发生太大改变,其主要变化有:
-
增加componentDidMount函数,并在其中调用一个应用redux-saga的action
-
将UI分离到另一个文件
redux-saga的新增代码
store/index.js
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'
import mySaga from './sagas'
const sagaMiddleware = createSagaMiddleware()
const dev_enhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = dev_enhancer(applyMiddleware(sagaMiddleware))
const store = createStore(
reducer,
enhancer
)
sagaMiddleware.run(mySaga)
export default store
代码相对于上次主要改动的地方有两个方面:
-
redux-saga的使用
-
增强函数实现dev tool于redux-saga的同时使用
redux-saga的使用
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import mySaga from './sagas'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(mySaga)
export default store
如果不使用调试工具,则redux-saga增加的代码如上,由此可见,redux-saga的初始化步骤为:
-
从redux引入applyMiddleware函数实现添加中间件
-
从redux-saga引入createSagaMiddleware函数用于生成一个sagaMiddleware
-
从自己创建的sagas.js文件中引入saga处理逻辑
-
创建sagaMiddleware
-
应用sagaMiddleware
-
启动sagaMiddleware
增强函数
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
const dev_enhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = dev_enhancer(applyMiddleware(thunk))
const store = createStore(
reducer,
enhancer
)
export default store
我们使用redux-thunk的初始化过程来讲解增强函数,同时记录下这个功能类似的中间件的初始化过程。
由于createStore函数无法同时接收dev tools与中间件,因此需要使用增强函数来实现这个功能。
增强函数,即返回一个函数的函数。在这个代码中,我们定义了一个dev tool的增强函数,并在其中传入了applyMiddleware函数作为参数,由此实现了两者的同时使用。
store/sagas.js
import { DID_MOUNT } from "./actionTypes"
import { takeEvery, put } from 'redux-saga/effects'
import { sagaAction } from './actionCreators'
function* mySaga() {
yield takeEvery(DID_MOUNT, didMount)
}
function* didMount() {
const action = sagaAction()
yield put(action)
}
export default mySaga
sagas主要暴露一个函数,在这个函数中建立中间件内action type与行为的对应关系。需要注意的是这个文件里的函数必须为generate函数。takeEvery与put必须使用yield语句。
在本例中,saga捕捉所有type为DID_MOUNT的action,并通过didMount函数进行处理,处理完成后,生成一个新的action,并通过put将action传入reducer进行处理。
thunk与saga的区别
在学习的过程中,我同时学习了两个相似的中间件,即thunk与saga。在我看来,两者主要的区别在于:
-
thunk结构简单,saga结构复杂
-
thunk引入后,action从必须为对象变成了既可以是对象又可以是函数,函数的action会运行内部的逻辑,进行处理后dispatch一个新的action给reducer,从而实现reducer作为纯函数不能实现的功能。saga并没有改变action的类型,而是作为一个假的reducer捕获指定类型的action,进行处理后put(即dispatch的API)一个新的action给reducer。
-
thunk并没有在项目结构中添加新的结构,处理逻辑保存在actionCreator中。saga增添sagas文件并将所有中间处理的逻辑放到这个文件中集中管理。