我有一个更新应用程序通知状态的操作。通常,该通知将是一个错误或某种类型的信息。然后,我需要在5秒后分派另一个动作,将通知状态返回到初始状态,因此没有通知。这背后的主要原因是提供通知在5秒后自动消失的功能。
我没有运气使用setTimeout和返回另一个动作,无法找到这是如何在线完成的。所以任何建议都是欢迎的。
我有一个更新应用程序通知状态的操作。通常,该通知将是一个错误或某种类型的信息。然后,我需要在5秒后分派另一个动作,将通知状态返回到初始状态,因此没有通知。这背后的主要原因是提供通知在5秒后自动消失的功能。
我没有运气使用setTimeout和返回另一个动作,无法找到这是如何在线完成的。所以任何建议都是欢迎的。
当前回答
Redux操作只能返回一个普通对象,而不是函数、回调或异步进程。为了通过web API(如timeout()方法)分派它们,你必须使用redux-thunk中间件。创建它是为了处理这样的流程。
首先通过文档配置redux-thunk 第二,这样改变你的动作创建器:
const yourAction = millisecond => dispatch => {
setTimeout(() => {
dispatch({
type: 'YOUR_ACTIION_TYPE',
payload: yourWhatEverPayload
})
}, millisecond)
}
其他回答
正确的方法是使用Redux坦克,这是一个 Redux的流行中间件,根据Redux坦克文档:
Redux坦克中间件允许你编写动作创建者 返回一个函数而不是一个动作。坦克可以用来拖延时间 一个动作的调度,或者只有在某种条件下才进行调度 是满足。内部函数接收存储方法的调度和 getState作为参数”。
基本上它会返回一个函数,你可以延迟分派或者把它置于条件状态。
所以像这样的东西会帮你完成工作:
import ReduxThunk from 'redux-thunk';
const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
function increment() {
return {
type: INCREMENT_COUNTER
};
}
function incrementAsync() {
return dispatch => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment());
}, 5000);
};
}
带有示例项目的存储库
目前有四个样本项目:
内联编写异步代码 提取异步操作创建器 使用Redux坦克 使用Redux Saga
公认的答案是棒极了。
但这里缺少了一些东西:
没有可运行的示例项目,只有一些代码片段。 没有其他替代方案的示例代码,例如: 回家的故事
所以我创建了Hello Async存储库来添加缺少的东西:
可运行的项目。您可以下载并运行它们而无需修改。 提供更多替代方案的示例代码: 回家的故事 回来的循环 ...
回家的故事
接受的答案已经提供了异步代码内联,异步动作生成器和Redux坦克的示例代码片段。为了完整起见,我提供了Redux Saga的代码片段:
// actions.js
export const showNotification = (id, text) => {
return { type: 'SHOW_NOTIFICATION', id, text }
}
export const hideNotification = (id) => {
return { type: 'HIDE_NOTIFICATION', id }
}
export const showNotificationWithTimeout = (text) => {
return { type: 'SHOW_NOTIFICATION_WITH_TIMEOUT', text }
}
行动是简单而纯粹的。
// component.js
import { connect } from 'react-redux'
// ...
this.props.showNotificationWithTimeout('You just logged in.')
// ...
export default connect(
mapStateToProps,
{ showNotificationWithTimeout }
)(MyComponent)
组件没有什么特别之处。
// sagas.js
import { takeEvery, delay } from 'redux-saga'
import { put } from 'redux-saga/effects'
import { showNotification, hideNotification } from './actions'
// Worker saga
let nextNotificationId = 0
function* showNotificationWithTimeout (action) {
const id = nextNotificationId++
yield put(showNotification(id, action.text))
yield delay(5000)
yield put(hideNotification(id))
}
// Watcher saga, will invoke worker saga above upon action 'SHOW_NOTIFICATION_WITH_TIMEOUT'
function* notificationSaga () {
yield takeEvery('SHOW_NOTIFICATION_WITH_TIMEOUT', showNotificationWithTimeout)
}
export default notificationSaga
saga基于ES6 Generators
// index.js
import createSagaMiddleware from 'redux-saga'
import saga from './sagas'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(saga)
与Redux坦克相比
Pros
你不会去回试的地狱。 您可以轻松地测试异步流。 你的行为保持纯净。
Cons
它依赖于相对较新的ES6 Generators。
如果上面的代码片段不能回答您的所有问题,请参考可运行项目。
如果希望对选择性操作进行超时处理,可以尝试中间件方法。 我遇到过一个类似的问题,有选择地处理基于承诺的行为,这个解决方案更灵活。
假设你的动作创造者是这样的:
//action creator
buildAction = (actionData) => ({
...actionData,
timeout: 500
})
Timeout可以在上述操作中包含多个值
以毫秒为单位的数字-用于指定的超时时间 True -用于固定的超时时间。(在中间件中处理) 未定义—用于立即分派
你的中间件实现看起来是这样的:
//timeoutMiddleware.js
const timeoutMiddleware = store => next => action => {
//If your action doesn't have any timeout attribute, fallback to the default handler
if(!action.timeout) {
return next (action)
}
const defaultTimeoutDuration = 1000;
const timeoutDuration = Number.isInteger(action.timeout) ? action.timeout || defaultTimeoutDuration;
//timeout here is called based on the duration defined in the action.
setTimeout(() => {
next (action)
}, timeoutDuration)
}
现在,您可以使用redux将所有操作路由到这个中间件层。
createStore(reducer, applyMiddleware(timeoutMiddleware))
你可以在这里找到一些类似的例子
为什么这么难呢?这只是UI逻辑。使用专用动作设置通知数据:
dispatch({ notificationData: { message: 'message', expire: +new Date() + 5*1000 } })
和一个专用的组件来显示它:
const Notifications = ({ notificationData }) => {
if(notificationData.expire > this.state.currentTime) {
return <div>{notificationData.message}</div>
} else return null;
}
在这种情况下,问题应该是“如何清理旧状态?”,“如何通知组件时间已更改”。
您可以实现一些TIMEOUT动作,该动作在组件的setTimeout上分派。
也许在显示新通知时清理它就可以了。
总之,应该有一些setTimeout,对吧?为什么不在组件中实现呢
setTimeout(() => this.setState({ currentTime: +new Date()}),
this.props.notificationData.expire-(+new Date()) )
其动机是“通知淡出”功能实际上是一个UI关注点。因此,它简化了业务逻辑的测试。
测试它是如何实现的似乎没有意义。只有验证通知何时应该超时才有意义。因此,更少的存根代码,更快的测试,更干净的代码。
在尝试了各种流行的方法(动作创造者,坦克,传奇,史诗,效果,自定义中间件)后,我仍然觉得可能还有改进的空间,所以我在这篇博客文章中记录了我的旅程,在React/Redux应用程序中我应该把我的业务逻辑放在哪里?
就像这里的讨论一样,我尝试对比和比较各种方法。最终,我引入了一个新的redux-logic库,它的灵感来自史诗、传奇故事和自定义中间件。
它允许您拦截验证、验证、授权的操作,并提供了一种执行异步IO的方法。
一些常见的功能可以简单地声明,如debashing、节流、取消,并且只使用来自最新请求的响应(takeLatest)。Redux-logic包装为您提供此功能的代码。
这使您可以随心所欲地实现核心业务逻辑。除非你愿意,否则你不必使用可观察对象或生成器。使用函数和回调,承诺,异步函数(async/await)等。
做一个简单的5s通知的代码是这样的:
const notificationHide = createLogic({ // the action type that will trigger this logic type: 'NOTIFICATION_DISPLAY', // your business logic can be applied in several // execution hooks: validate, transform, process // We are defining our code in the process hook below // so it runs after the action hit reducers, hide 5s later process({ getState, action }, dispatch) { setTimeout(() => { dispatch({ type: 'NOTIFICATION_CLEAR' }); }, 5000); } });
在我的repo中,我有一个更高级的通知示例,其工作原理类似于Sebastian Lorber所描述的,您可以将显示限制为N个项目,并通过任何排队的项目进行旋转。Redux-logic通知示例
我有各种redux-logic jsfiddle现场的例子,以及完整的例子。我还在继续写文档和例子。
我很想听听你的反馈。