参考资料
中间件
什么是Redux的中间件
- 中间件指的是action 和 store 中间
- 中间件实现是对store的dispatch方法的升级
几个常见中间件的作用
对dispatch方法的升级
- redux-thunk:使store接收的action可以是函数(初始只能是对象)。当action是函数时,直接执行该函数
- redux-saga:也是处理异步逻辑,把异步逻辑单独放在一个文件中管理
- redux-log:每次dispatch时,在控制台输出内容
Redux-thunk
使用说明文档
npm install redux-thunk --save
使用redux-thunk实现ajax流程
src
store
index.js
// store/index.js配置(直接使用)
import { createStore, applyMiddleware } from 'redux' // 1 引入applyMiddleware
import thunk from 'redux-thunk'; // 2 引入thunk
import reducer from './reducer'
const store = createStore(reducer, applyMiddleware(thunk)) // 3 使用
export default store
// store/index.js配置(配合window.__REDUX_DEVTOOLS_EXTENSION__ 使用)
import { createStore, applyMiddleware, compose } from 'redux' //1 引入applyMiddleware, compose
import thunk from 'redux-thunk' // 2 引入thunk
import reducer from './reducer'
// 3 composeEnhancers enhancer
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
}) : compose
const enhancer = composeEnhancers(
applyMiddleware(thunk) // other store enhancers if any
)
const store = createStore(reducer, enhancer) // 使用
export default store
actionCreators
在actionCreators创建一个返回函数(如ajax请求)的action
// actionCreators
export const initItemAction = (value) => ({
type: INIT_TODO_ITEM,
value
})
// 当使用redux-thunk后,action不仅可以是对象,还可以是函数
// 返回的如果是方法会自动执行
// 返回的方法可以接收到dispatch方法,去派发其它action
export const getTodoList = () => {
return (dispatch) => {
axios.get('/initList').then(res => {
const action = initItemAction(res.data);
dispatch(action);
})
}
}
TodoList
3.component组件中派发acition
import React, { Component } from 'react'
import store from './store'
import {getListData} from './store/actionCreators'
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState()
store.subscribe(this.handleStateChange)
}
render() {
.....
}
componentDidMount() { // 组件挂载后派发store
const action = getTodoList()
store.dispatch(action)
}
handleStateChange() {
this.setState(store.getState())
}
}
export default TodoList
4.store交给reduce处理,reducer发现竟然是个函数,那不好意思,直接给你执行了!!!
在第2步骤里,return函数的action里,函数直接执行,获取异步的结果再次派发action给store,store检查发现这次是对象(而不是函数了),那就按正常流程更新store
Redux-saga
npm install --save redux-saga
yarn add redux-saga
src
store
index.js
// src/store/index.js
import { createStore, applyMiddleware } from 'redux'
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'
import mySaga from './sagas'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(reducer, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(mySaga)
export default store
saga.js
// src/store/saga.js文件的配置
import { put, takeEvery } from 'redux-saga/effects'
import { GET_LIST_DATA} from './actionTypes'
import { initList } from './actionCreators'
function* fetchUser() {
try {
const res = yield new Promise((resolve, reject) => { // 3 回调里执行异步获取数据
let arr = ['hello', 'liukun']
resolve(arr)
})
const action = initList(res)
console.log('action', res)
yield put(action) // 4 再次派发action更新data
} catch (e) {
console.log('网络异常')
}
}
function* mySaga() { // 2 saga监控到TodoList派发到anction,执行回调fetchUser
yield takeEvery(GET_LIST_DATA, fetchUser);
}
export default mySaga;
actionCreators.js
// src/store/actionCreators.js
export const initList = (value) => ({ // res
import { INPUT_CHANGE_VALUE, BTN_SUBMIT, LIST_DELETE, GET_LIST_DATA, INIT_LIST } from './actionTypes'
export const inputValueChange = (value) => ({
type: INPUT_CHANGE_VALUE,
value
})
export const btnSubmit = () => ({
type: BTN_SUBMIT
})
export const listDelete = (index) => ({
type: LIST_DELETE,
index
})
export const listData = (value) => ({
type: GET_LIST_DATA,
value
})
export const initList = (value) => ({
type: INIT_LIST,
value
})
export const getListData = () => ({
type: GET_LIST_DATA
})
reducer.js
// src/store/reducer.js
// 处理action更新的data
import { INPUT_CHANGE_VALUE, BTN_SUBMIT, LIST_DELETE, INIT_LIST } from './actionTypes'
const defaultState = {
inputValue: '',
list: []
}
export default (state = defaultState, action) => {
const newState = JSON.parse(JSON.stringify(state))
const { type } = action
switch(type) {
case INPUT_CHANGE_VALUE:
newState.inputValue = action.value
break
case BTN_SUBMIT:
newState.list.push(newState.inputValue)
newState.inputValue = ''
break
case LIST_DELETE:
newState.list.splice(action.index, 1)
break
case INIT_LIST: // 5 reducer处理派发过来的action并更新state
newState.list = [...newState.list, ...action.value]
break
default:
return state
}
return newState
}
TodoList.js
// src/TodoList.js
// 派发action
import React, { Component } from 'react'
import store from './store'
import TodoListUI from './TodoListUI'
import { inputValueChange, btnSubmit, listDelete, getListData} from './store/actionCreators'
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this)
this.handleBtnSubmit = this.handleBtnSubmit.bind(this)
this.handleListDelete = this.handleListDelete.bind(this)
this.handleStateChange = this.handleStateChange.bind(this)
store.subscribe(this.handleStateChange)
}
render() {
return (
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
handleInputChange={this.handleInputChange}
handleBtnSubmit={this.handleBtnSubmit}
handleListDelete={this.handleListDelete}
/>
)
}
componentDidMount() { // 1 TodoList文件派发action
const action = getListData()
store.dispatch(action)
}
handleStateChange() {
this.setState(store.getState())
}
handleInputChange(e) {
const action = inputValueChange(e.target.value)
store.dispatch(action)
}
handleBtnSubmit() {
const action = btnSubmit()
store.dispatch(action)
}
handleListDelete(index) {
const action = listDelete(index)
store.dispatch(action)
}
}
export default TodoList
Redux-saga中间件执行流程
-
组件加载完成后,把一个普通的action对象派发给store;
-
因使用了redux-saga中间件,所以会被sagas.js中的generator函数匹配到,并交给对应的函数(一般也是generator函数)处理;
-
sagas.js的函数拿到结果后,使用redux-saga的put方法再次派发一个普通action对象给store;
-
sagas.js中没有匹配到对应的类型,则store交由reducer处理并更新store的状态。
React-redux
npm install react-redux --save
src/index.js配置
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import store from './store'
import TodoList from './TodoList'
import { Provider } from 'react-redux' // 1 引入Provider
const App = (
<Provider store={store}> {/* Provider和store连接,之后Provider里的组件都可以获取store里的数据 */}
<TodoList />
</Provider>
)
ReactDOM.render(App, document.getElementById('root'))
src/TodoList.js使用
import React from 'react'
import { connect } from 'react-redux' // 2 引入connect
import { Input, List, Button } from 'antd'
import 'antd/dist/antd.css'
import { inputValueChange, btnSubmit, listDelete} from './store/actionCreators'
const TodoList = (props) => {
return (
<div>
<Input
style={{width: 300}}
placeholder='todo info'
value={props.inputValue}
onChange={props.handleInputChange}
/>
<Button
onClick={props.handleBtnSubmit}
>提交</Button>
<List
style={{width: 300}}
bordered
dataSource={props.list}
renderItem={(item, index) =>(
<List.Item onClick={() => {props.handleListDelete(index)}}>{item}</List.Item>
)}
>
</List>
</div>
)
}
const mapStateToProps = (state) => {
return { // 3 mapStateToProps:把store里的stata映射到当前组件的props
inputValue: state.inputValue,
list: state.list
}
}
const mapDispatchToProps = (dispatch) => {
return { // 3 mapDispatchToProps:把store里的dispatch方法映射到当前组件的props
handleInputChange(e) {
console.log(e.target.value)
const action = inputValueChange(e.target.value)
dispatch(action)
},
handleBtnSubmit() {
const action = btnSubmit()
dispatch(action)
},
handleListDelete(index) {
const action = listDelete(index)
dispatch(action)
}
}
}
// 4 把当前组件和store进行连接并export
export default connect(mapStateToProps, mapDispatchToProps)(TodoList)