redux
中间件
官方文档
在redux
里,action
仅仅是携带了数据的普通js对象
。action creator
返回的值是这个action
类型的对象。然后通过store.dispatch()
进行分发。同步的情况下一切都很完美,也就是说,默认情况下,Redux
自身只能处理同步数据流。但是在实际项目开发中,状态的更新、获取,通常是使用异步操作来实现, 但是reducer
无法处理异步的情况。那么我们就需要在action
和reducer
中间架起一座桥梁来处理异步。这就是middleware
(中间件)。
由此可见,在redux
中,中间件的的核心功能就是:在 action
被发起之后,到达 reducer
之前的一些扩展,主要增强store.dispatch
的能力
中间件的执行流程
由上图我们可以看出,所谓redux中间件
其实就是指的是action
和store
之间的流程加工。即dispatch
的封装和升级。
中间件的作用以及触发时机
处理具有副作用(side effect)的功能,比如,异步操作就是最常见的 side effect
中间件说明:
- 中间件,可以理解为处理一个功能的中间环节
- 中间件的优势:可以串联、组合,在一个项目中使用多个中间件
Redux
中间件用来处理 状态 更新,也就是在 状态 更新的过程中,执行一系列的相应操作
中间件的触发时机
Redux
中间件执行时机:在 dispatching action
和 到达 reducer
之间。
没有中间件:dispatch(action) => reducer
使用中间件:dispatch(action) => 执行中间件代码 => reducer
原理:
封装了 redux
自己的 dispatch
方法
- 没有中间件:
store.dispatch()
就是 Redux
库自己提供的原生 dispatch
方法,用来发起状态更新
- 使用中间件:
store.dispatch()
就是中间件封装处理后的 dispatch
,但是,最终一定会调用 Redux
自己的 dispatch
方法发起状态更新
redux-thunk
- 官方文档
Redux-thunk
是 redux
的中间件,它主要设计用来处理项目中的异步操作
,如获取接口数据等,过去异步操作
都会写在组件中,请求到数据再创建 action
。这使得异步操作
遍布很多组件文件,中,维护不便,且不易于自动化测试。
Redux-thunk
主要理念是:将所有异步操作
都放在action
里面去处理,与业务组件代码解耦,而要实现这个理念,需要 action
处理异步逻辑,需要它是一个函数。
实现逻辑
- 原生的
Redux
中,action
(actionCreator
)内部返回的是一个对象,但是redux-thunk
中间件可以处理函数形式的 action
,由此我们就可以在这个函数形式的action
中处理异步操作
React
内部通过判断你action
中返回的是一个对象还是一个函数来执行操作,如果是返回一个对象则直接调用dispatch
来修改状态,如果是一个函数,则使用者可以执行决定调用dispatch
的时机,即我们可以等待异步操作完成后在调用dispatch
修改状态(redux-thunk会给我们在返回的函数中提供dispatch
)
import axios from 'axios'
function getDataAction(){ axios.get('/xxx/xxx').then(res=>{ console.log('数据请求成功',res) return { type:'getData', value:res, } }) }
export default getDataAction
function getDataAction2(){
return (dispatch,getState)=>{ axios.get('/xxx/xxx').then(res=>{ console.log('数据请求成功!',res) dispatch ({ type:'getData', value:res, }) }) } }
export default getDataAction
|
使用流程
- 安装
redux-thunk
- 在全局的
store
中配置中间件,类似于Vue
中的Vue.use()
import {applyMiddleware, combineReducers, createStore} from 'redux'
import pageNameReducer from './reducer/pageNameReducer' import isShowBottomReducer from './reducer/isShowBottomReducer'
import ReduxThunk from 'redux-thunk'
const reducer = combineReducers({ pageNameReducer, isShowBottomReducer })
const store = createStore(reducer,applyMiddleware(ReduxThunk))
export default store
|
actionCreator
自定义action
import axios from 'axios'
function getDataAction2(){
return (dispatch,getState)=>{ axios.get('https://dog.ceo/api/breeds/image/random').then(res=>{ console.log('数据请求成功!',res) dispatch ({ type:'getData', value:res.data.message, }) }) } }
export default getDataAction2
|
home.js
(home组件)
import React, { Component } from 'react'
import { HashRouter as Router, Route, Redirect, Switch, NavLink } from 'react-router-dom' import store from '../../store/index.js'
import About from './about.js' import Info from './info.js' import Footer from './footer.js' import NotFound from './404.js'
export default class home extends Component { componentDidMount(){
store.subscribe(()=>{ console.log('home组件监听中....'); this.setState({ isShow:store.getState().isShowBottomReducer.isShow }) }) } state = { isShow:store.getState().isShowBottomReducer.isShow } render() { console.log('是否展示底部区域',store.getState()); return ( <div> <h1>首页</h1> <p>(去到信息页关闭底部,去到关于页开启底部)</p> <div style={{ width: '100%', height: '400px', backgroundColor: 'yellowgreen' }}> <Router> <ul> <li><NavLink to={'/home/info'}>info</NavLink></li> <li><NavLink to={'/home/about'}>about</NavLink></li> </ul> {/* 使用Switch来解决每次页面刷新,重定向功能执行的bug(模糊匹配) */} <Switch> <Route path="/home/about" component={About} /> <Route path="/home/info" component={Info} />
<Redirect from="/" to="/home/info" exact /> {/* 匹配不到的页面则展示404 */} <Route component={NotFound} /> </Switch> </Router> </div> {/* 通过判断全局store中的isShow来执行是否展示Footer组件 */} {this.state.isShow && <Footer />} </div> ) } }
|
about.js
(展示图片的组件)
import React ,{useEffect,useState}from 'react' import store from '../../store/index.js' import getDataAction from '../../store/actionCreator/getDataAction.js';
export default function About() { console.log('组件创建时全局store中的图片数据',store.getState().dataReducer.data); useEffect(()=>{ console.log('进入About组件'); if(store.getState().dataReducer.data === ''){ store.dispatch(getDataAction()) console.log('全局store中没有数据!'); }else{ console.log('使用缓存中的数据!'); }
return ()=>{ console.log('退出About组件'); unsubscribe() } },[]) const [img,setImg] = useState(store.getState().dataReducer.data)
var unsubscribe = store.subscribe(()=>{ console.log('about组件订阅中.....',store.getState().dataReducer.data); })
return ( <div> <img src={`${img}`} alt=""/> </div> ) }
|
结果展示: