如何使用setTimeout去调用Redux的action

本问题选自Stack overflow中llja提出的一个问题,由于本人翻译水平有限,所以有些不明白的地方,可以直接访问这个问题的网址,原地址
有涉及到侵权的问题,请告知我,我会第一时间删除本篇文章,本人的邮箱:aleenli1992@gmail.com

原问题:

如何使用setTimeout去调用Redux的action

我有个更新我的APP通知状态的动作。通常通知的种类是一个错误或者信息。我需要五秒后发送一个动作,之后通知状态返回最初的样式,但是没有通知。最主要的实现是提供一个功能,一个可以在5秒之后通知自动消失的功能。

在使用setTimeout的时候,正在返回其他动作但是无法找到谁在线上完成了。所以希望大家给点建议。

原答案:

不要掉进一个库应该能够作任何事情的陷阱里,如果你想在JS中使用一个计时器,你就需要使用setTimeout。没什么理由证明Redux和其他库的不一样。

Redux确实提供了一些解决异步操作的替用方法,但是你只能用在当你意识到你复制了太多代码的时候。除非你有这个问题,使用自带的语言是最简单的解决方案。

在线写异步代码

只是迄今为止最简单的方法,并且没什么特别的Redux代码。

1
2
3
4
store.dispatch({ type: 'SHOW_NOTIFIACTION'. text: 'You logged in.'})
setTimeout(() => {
store.dispatch({ type: 'HIDE_NOTIFIACTION '})
})

同样,一个连接组件的内部:

1
2
3
4
this.props.dispatch({type: 'SHOW_NOTIFIACTION'. text: 'You logged in.'})
setTimemout(() => {
this.props.dispatch({ type: 'HIDE_NOTIFICATION' })
},5000)

这个连接组件的唯一区别是你通常无法访问储存本身,而是将任意一种dispatch()或者特定的操作创建者去引入为props,无论怎样,这对与我们没有任何区别。

如果你不喜欢当你想在不同的组件完成相同的操作时有错误的话,你可能需要将操作的组件提取出来,而不是将需要调遣的对象写在函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function showNotification(id ,text){
return { type: 'SHOW_NOTIFICATION', id, text }
}
function hideNotification(id){
return {type: 'HIDE_NOTICATION' ,id}
}
let nextNotificicationId = 0
export function showNotficationWithTimeout( dispatch, text){
// 分配IDS去通知 允许reducer忽视HIDE_NOTIFICATION
// 给通知定义一个不是当前的变量
// 或者我们可以储存一个间隔的ID去调用
// clearInterval(),但是我们可以在这个单独的空间里操作
conts id = nextNotificationId++
dispatch(showNotification(id, text))
setTiomeout(() => {
dispatch(hideNotfication(id))
},5000)
}

现在组件就可以使用showNotficationWithTimeout,而不用复制这个逻辑或者有不同的通知在这个时间轴上。

1
2
3
4
5
//component.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')
//otherComponent.js
showNotificationWithTimeout(this.props.dispath, 'You just logged out.')

为什么showNotificationWithTimeout接受dispath作为第一个参数呢?这是因为他需要通过dispatch的操作去储存。通常一个组件有一个入口去dispatch,但是如果我们想要外部函数来去控制这个dispatch,我们就需要对这个dispatch进行控制。

如果你的一些模块有一个独立的储存空间,你只要进入它并且直接替代dispatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//store.js
export default createStore(reducer)
//actions.js
import store from './store'
//...
let nextNotificationId = 0
export function showNotficationWithTimeout(text){
export function showNotificationWithTimeout(text){
const id = notNotificationId++
store.dispacth(showNotification(id,tetx))
setTimeout(() => {
store.dispatch(hideNotification(id))
},5000)
}
}
// component.js
showNotificationWithTimeout('You just logged in.')
// otherComponent.js
showNotificationWithTimeout('You just logged out.')

这样看起来比较简洁但是我们们并不推荐这样的处理。主要原因是我们不喜欢这个,因为它被迫的是储存区域变成了一个独立区。因为这样使得sever rendeing执行起来非常困难。在服务器中,你希望每个请求都有自己的储存区,从而使不同的用户获得不同的预加载数据。

一个独立的储存区也让测试变得困难。你不能长时间的去mock一个储存区当你测试运行的时候,因为他们在一个特定的模块中应用了特定的真是储存区。你不能每一次都从外部调整它的state。

虽然在技术上你可以从一个模块中提取出一个独立的储存区,但是我们很失望。不要这样做,除非你确定你的app将永远不会添加服务器的rendering。

回到先前这个版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// actions.js
// ...
let nextNotificationId = 0
export function showNotificationWithTimeout(dispatch, text) {
const id = nextNotificationId++
dispatch(showNotification(id, text))
setTimeout(() => {
dispatch(hideNotification(id))
}, 5000)
}
// component.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')
// otherComponent.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged out.')

这样解决了问题,在时间轴上既符合逻辑也保存了我们需要的数据。

Thunk Middleware

一个简单的app,交互应该足够。如果你乐意使用middleware就不要担心它。

大多数的app,无论如何,你都会发现有很多不足之处。

举一个例子,我们需要通过dispatch时,他看起来很悲催。