关于 redux-thunk 的作用,认识,理解
看这篇文章之前,如果你已经看到一些
redux-thunk
的教程,是不是觉得一头雾水,redux-thunk 到底有什么作用,用在哪里,代码不仅没有简化,反而还增加?基于我初学的一些疑惑和后来疯狂的百度,总结了一下
首先来一段代码示例
如果我们有一个异步请求,获取数据展示在页面上。假设 redux 都是已经写好了。我们只模拟请求阶段
// App.js
import React, {
Component } from 'react'
import store from './store/index'
import axios from 'axios'
class App extends Component {
constructor(props) {
super(props)
this.state = store.getState()
// store 监听更新,并且自动赋值到页面上
store.subscribe(() => this.setState(store.getState()))
}
// 在页面组件挂载后,请求一个模拟的接口,获取一个todoList列表
componentDidMount() {
axios
.get('http://rap2.taobao.org:38080/app/mock/246209/todoList')
.then(res => {
store.dispatch({
type: 'GET_LIST', value: res.data.data })
})
.catch(res => {
})
}
render() {
return (
<ul>
{
this.list.map((item, index) => {
return <li key={
index}>{
item}</li>
})}
</ul>
)
}
}
export default App
先来看下这段代码有什么问题:
-
在生命周期函数中请求了一个接口,那如果这个页面渲染需要多个接口呢,那生命周期代码量就会非常的大(可以把这些请求抽成方法啊,然后生命周期中只调用方法就简洁了)。没错,写成方法真是我们要做的
-
不利于测试,在我们没写成方法之前,如果我们想单元测试下这个接口,模拟一些数据,我们很难在生命周期中模拟
使用 redux-thunk
来优化代码
新建一个 js 文件,来统一放我们请求/操作的代码
- 我们定义一个
getTodoList
方法
// src/store/actionCreators.js
// 这个方法看上去像一个闭包。因为redux-thunk是可以接收一个函数的,所以我们来返回一个函数
export const getTodoList = () => {
return dispatch => {
axios
.get('http://rap2.taobao.org:38080/app/mock/246209/todoList')
.then(res => {
console.log(res.data.data)
dispatch({
type: GET_LIST, value: res.data.data })
})
.catch(res => {
})
}
}
- 在 App.js 页面上使用
// App.js
import React, {
Component } from 'react'
import store from './store/index'
// ! 异同 ! 这里我们不在需要引入axios。而是引入我们的方法
import {
getTodoList } from './store/actionCreators'
class App extends Component {
constructor(props) {
super(props)
this.state = store.getState()
// store 监听更新,并且自动赋值到页面上
store.subscribe(() => this.setState(store.getState()))
}
componentDidMount() {
// ! 异同 ! 这里还是在生命周期获取数据。
// 不过这里我们执行 getTodoList() 返回的是一个函数
store.dispatch(getTodoList())
}
render() {
return (
<ul>
{
this.list.map((item, index) => {
return <li key={
index}>{
item}</li>
})}
</ul>
)
}
}
export default App
对比着看,2 段代码没有什么不同,反而好像了些代码,那下面我们就来细细分析
redux-thunk 作用是什么
redux-thunk 可以让
store.dispatch
变成可以接收一个函数/一个对象的中间件
- redux-thunk 源码
function createThunkMiddleware(extraArgument) {
return ({
dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument)
}
return next(action)
}
}
const thunk = createThunkMiddleware()
thunk.withExtraArgument = createThunkMiddleware
export default thunk
让原本只能接受对象的 store.dispatch 变成可以接收对象/方法,并且如果接收了一个方法后自动执行该方法,而不触发 redux 的 store 更新。
这有点拗口,来一个张图:
而且 redux-thunk 在执行我们的函数后,可以给我们返回 dispatch
.在 redux-thunk 源码的第四行。那么我们可以在异步回调后直接拿到 dispatch
来继续派发我们的 redux 的事件,并且把异步函数抽象成了一个方法,使用 store.dispatch
来触发该方法。
譬如我们有一个加入购物车功能的接口,在商品详情可以加入购物车,在购物车可以添加商品的数量,那这个接口是可以复用的,抽离了之后,就可以在不同界面一起调用这个接口。从而实现代码复用
如果说 redux-thunk 是为了异步代码复用,为什么不定义一个方法调用,一定要用 redux-thunk 呢?
难道我们写成一个公共函数,然后在函数中引入 sotre 他不香吗?这样全部方法都可以获得 store 对象,也能调用 store.dispatch 方法来派发事件啊
这个事情要从 redux
设计说起:
-
它强制了 store 必须是一个单例
:这对服务端渲染很不友好,因为对于不同的用户或者说请求,它都应该有个独立的 store -
不易于测试
:因为使用了外部引用的 store,你没办法方便地 mock 一个 store 用来测试,也不能控制 store 的状态
ps: 以上 2 短话摘录自:为什么要使用 redux-thunk。包括上面的图也是盗用这位大佬的。
总结
redux-thunk 的功能介绍就说道这里了。在刚开始学的时候,的确很难理解,明明代码没简化,还多引了一个插件,这到底图什么?
一切为了代码复用,可能代码量还少,看不出来优势,等到多个地方用到同一段代码,就知道代码复用是多爽的一件事,其次就是 redux-thunk 强化了 store.dispatch 方法。而且遵循了 redux
的强制store必须是一个单例
的规则。也方便做局部测试