redux-promise

  • 官方文档

  • redux-promise与上篇博客提到的redux-thunk是非常相似的,都是用与处理异步操作,不过相较于thunk,可能promise的用法更为多样一些,可以结合async,await等来结合使用

  • redux-thunk不同的是,在redux-promise中,如果action是一个promise, 则会等待promise完成,将完成的结果作为action触发(可以直接return,也可以手动reslove),如果action不是一个promise,则判断其payload是否是一个promise,如果是,等待promise完成,然后将其作为payload的值触发。

案例展示:

  • 沿用上一篇博客的案例,axios请求小狗的照片
  1. 安装redux-promise
npm i redux-promise
  1. 在全局的store中配置中间件,类似于Vue中的Vue.use()
// 1. 引入redux
import {applyMiddleware, combineReducers, createStore} from 'redux'

// 2. 引入其他的小Reducer
import pageNameReducer from './reducer/pageNameReducer'//处理关于页面标题的reducer
import isShowBottomReducer from './reducer/isShowBottomReducer'//处理底部区域展示的reducer
import dataReducer from './reducer/dataReducer.js'//处理axios异步请求数据的reducer

// 3. 引入中间件
import ReduxThunk from 'redux-thunk'//引入redux-thunk中间件
import ReduxPromise from 'redux-promise'//引入redux-promise中间件

// 4. 合并reducer
const reducer = combineReducers({
pageNameReducer,
isShowBottomReducer,
dataReducer
})

/*
由于创建的state是不能直接放到创建的store中的, 需要通过reducer将数据添加到store中,
因此创建store时必须创建reducer;reducer函数的返回值, 会作为store之后存储的state,
只要调用dispatch就会重新执行reducer函数,reducer是一个纯函数,不可以直接修改state,
*/

// 5. 创建全局store
const store = createStore(reducer,applyMiddleware(ReduxThunk,ReduxPromise))//并应用中间件

// 6. 导出store
export default store
  1. actionCreator自定义action
// 接口地址:随机生成一个狗图(vpn):https://dog.ceo/api/breeds/image/random
// 1. 引入axios
import axios from 'axios'

/* 2. 使用redux中间件结合actionCreator创建的action实现异步处理操作 */
async function getDataAction2() {

/* ------------------- (1)redux-promise中间件处理异步操作 ---------------------*/
// 1.简单写法,因为axios本身就是promise对象,它是基于promise封装的
// return axios.get('https://dog.ceo/api/breeds/image/random').then(res => {
// console.log('数据请求成功!', res.data.message)
// return {
// type: 'getData',// 触发修改全局store中状态的字段
// value: res.data.message,// 返回的数据
// }
// })

// 2.完整写法,手动使用reslove执行dispatch
// return new Promise(reslove=>{ // return action
// axios.get('https://dog.ceo/api/breeds/image/random').then(res=>{
// console.log('数据请求成功!',res);
// // 如果action是一个promise,则将其resolve的值dispatch
// reslove ({//手动使用reslove传递action对象给dispatch
// type: 'getData',// 触发修改全局store中状态的字段
// value: res.data.message,// 返回的数据
// })
// })
// })

// 3. 结合async await来使用
var data = await axios.get('https://dog.ceo/api/breeds/image/random').then(res => {
console.log('数据请求成功!', res.data.message)
return {
type: 'getData',// 触发修改全局store中状态的字段
value: res.data.message,// 返回的数据
}
})
return data



/* ------------------- (2)redux-thunk中间件处理异步操作 ------------------------ */
/*
注意:此处返回的是一个函数,返回的函数有两个参数:
第一个参数:dispatch 函数,用来分发 action
第二个参数:getState 函数,用来获取 redux 状态
*/
// return (dispatch,getState)=>{// 处理异步操作,等待数据返回成功在执行dispatch操作(相当于转为同步)
// // 使用axios发送数据请求(异步操作)
// axios.get('https://dog.ceo/api/breeds/image/random').then(res=>{
// console.log('数据请求成功!',res)
// //判断数据请求完成后再通过dispatch通知reducer修改状态
// dispatch ({ // 数据请求成功action后返回原生的对象
// type:'getData',// 触发修改全局store中状态的字段
// value:res.data.message,// 返回的数据
// })
// })
// }
}

export default getDataAction2 // 将创建的action暴露出去
  1. 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'//引入全局store
// 引入对应的组件
import About from './about.js'//关于页
import Info from './info.js'//个人信息页
import Footer from './footer.js'//底部区域
import NotFound from './404.js'//404页面

export default class home extends Component {
componentDidMount(){
/*
通过store.subscribe()函数可以监听store中的数据变化,
store.subscribe()函数的参数接收一个函数, 该函数在store数据发生更新自动回调
*/

store.subscribe(()=>{
console.log('home组件监听中....');
// 修改组件中state的状态重新渲染组件
this.setState({
// 使用reducer合并则需要多获取对应的小reducer在获取对应的状态
isShow:store.getState().isShowBottomReducer.isShow
})
})
}
state = {
// 我们可以在其他文件中通过 store.getState 来获取当前的state;
// 使用reducer合并则需要多获取对应的小reducer在获取对应的状态
isShow:store.getState().isShowBottomReducer.isShow // 获取全局store中的状态
}
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>
)
}
}
  1. about.js(展示图片的组件)
import React ,{useEffect,useState}from 'react'
import store from '../../store/index.js'//引入全局store
import getDataAction from '../../store/actionCreator/getDataAction.js';

export default function About() {
// 使用reducer合并则需要多获取对应的小reducer在获取对应的状态
console.log('组件创建时全局store中的图片数据',store.getState().dataReducer.data);
// 模拟生命周期
useEffect(()=>{//组件挂载
console.log('进入About组件');
// 判断全局store中是否有图片数据
if(store.getState().dataReducer.data === ''){
store.dispatch(getDataAction())
console.log('全局store中没有数据!');
}else{
console.log('使用缓存中的数据!');
}

return ()=>{//组件销毁
console.log('退出About组件');
// 离开About组件修改全局store中的payload为false(关闭底部区域)
unsubscribe()//取消订阅
}
},[])
// 使用hooks创建调用state
// 使用reducer合并则需要多获取对应的小reducer在获取对应的状态
const [img,setImg] = useState(store.getState().dataReducer.data)

// 我们也可以在对应的组件中设置store订阅(设置订阅与取消订阅)
var unsubscribe = store.subscribe(()=>{
console.log('about组件订阅中.....',store.getState().dataReducer);
})

return (
// 展示全局store中的pageName
<div>
<img src={`${img}`} alt=""/>
</div>
)
}

结果展示:

image

Redux开发者工具

下载完成后可见浏览器控制栏有redux字样

image

配置文档配置全局store中指定的代码来获取状态

// 1. 引入redux
import {applyMiddleware, combineReducers, createStore, compose } from 'redux'

// 2. 引入其他的小Reducer
import pageNameReducer from './reducer/pageNameReducer'//处理关于页面标题的reducer
import isShowBottomReducer from './reducer/isShowBottomReducer'//处理底部区域展示的reducer
import dataReducer from './reducer/dataReducer.js'//处理axios异步请求数据的reducer

// 3. 引入中间件
import ReduxThunk from 'redux-thunk'//引入redux-thunk中间件
import ReduxPromise from 'redux-promise'//引入redux-promise中间件

// 4. 合并reducer
const reducer = combineReducers({
pageNameReducer,
isShowBottomReducer,
dataReducer
})

/*
由于创建的state是不能直接放到创建的store中的, 需要通过reducer将数据添加到store中,
因此创建store时必须创建reducer;reducer函数的返回值, 会作为store之后存储的state,
只要调用dispatch就会重新执行reducer函数,reducer是一个纯函数,不可以直接修改state,
*/

// 5. 创建全局store(配置redux开发者工具)
//配置redux开发者工具
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer,composeEnhancers(// composeEnhancers(高级模式,可监视同步与异步)
applyMiddleware(ReduxThunk,ReduxPromise)//并应用中间件
))

// 6. 导出store
export default store

结果展示:

image