我有一个更新应用程序通知状态的操作。通常,该通知将是一个错误或某种类型的信息。然后,我需要在5秒后分派另一个动作,将通知状态返回到初始状态,因此没有通知。这背后的主要原因是提供通知在5秒后自动消失的功能。

我没有运气使用setTimeout和返回另一个动作,无法找到这是如何在线完成的。所以任何建议都是欢迎的。


当前回答

这可能有点离题,但我想在这里分享它,因为我只是想在给定超时后从状态中删除警报,即自动隐藏警报/通知。

我最终在<Alert />组件中使用setTimeout(),以便它可以在给定id上调用和分派REMOVE操作。

export function Alert(props: Props) {
  useEffect(() => {
    const timeoutID = setTimeout(() => {
      dispatchAction({
        type: REMOVE,
        payload: {
          id: id,
        },
      });
    }, timeout ?? 2000);
    return () => clearTimeout(timeoutID);
  }, []);
  return <AlertComponent {...props} />;
}

其他回答

如果希望对选择性操作进行超时处理,可以尝试中间件方法。 我遇到过一个类似的问题,有选择地处理基于承诺的行为,这个解决方案更灵活。

假设你的动作创造者是这样的:

//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))

你可以在这里找到一些类似的例子

Redux操作只能返回一个普通对象,而不是函数、回调或异步进程。为了通过web API(如timeout()方法)分派它们,你必须使用redux-thunk中间件。创建它是为了处理这样的流程。

首先通过文档配置redux-thunk 第二,这样改变你的动作创建器:

const yourAction = millisecond => dispatch => {
   setTimeout(() => {
      dispatch({
         type: 'YOUR_ACTIION_TYPE',
         payload: yourWhatEverPayload
      })
   }, millisecond)
}

你可以用redux-thunk做到这一点。redux文档中有关于setTimeout等异步操作的指南。

为什么这么难呢?这只是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关注点。因此,它简化了业务逻辑的测试。

测试它是如何实现的似乎没有意义。只有验证通知何时应该超时才有意义。因此,更少的存根代码,更快的测试,更干净的代码。

带有示例项目的存储库

目前有四个样本项目:

内联编写异步代码 提取异步操作创建器 使用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。

如果上面的代码片段不能回答您的所有问题,请参考可运行项目。