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

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


当前回答

我知道这个问题有点老了,但我将介绍另一种解决方案,使用还原可观测。史诗。

引用官方文件:

什么是可还原可观察?

基于RxJS 5的Redux中间件。合成和取消异步操作 创造副作用等等。

史诗是还原可观察的核心原语。

它是一个接受操作流并返回流的函数 的行动。行动进,行动出。

简而言之,您可以创建一个通过流接收操作的函数,然后返回一个新的操作流(使用常见的副作用,如超时、延迟、间隔和请求)。

让我发布代码,然后再详细解释一下

store.js

import {createStore, applyMiddleware} from 'redux'
import {createEpicMiddleware} from 'redux-observable'
import {Observable} from 'rxjs'
const NEW_NOTIFICATION = 'NEW_NOTIFICATION'
const QUIT_NOTIFICATION = 'QUIT_NOTIFICATION'
const NOTIFICATION_TIMEOUT = 2000

const initialState = ''
const rootReducer = (state = initialState, action) => {
  const {type, message} = action
  console.log(type)
  switch(type) {
    case NEW_NOTIFICATION:
      return message
    break
    case QUIT_NOTIFICATION:
      return initialState
    break
  }

  return state
}

const rootEpic = (action$) => {
  const incoming = action$.ofType(NEW_NOTIFICATION)
  const outgoing = incoming.switchMap((action) => {
    return Observable.of(quitNotification())
      .delay(NOTIFICATION_TIMEOUT)
      //.takeUntil(action$.ofType(NEW_NOTIFICATION))
  });

  return outgoing;
}

export function newNotification(message) {
  return ({type: NEW_NOTIFICATION, message})
}
export function quitNotification(message) {
  return ({type: QUIT_NOTIFICATION, message});
}

export const configureStore = () => createStore(
  rootReducer,
  applyMiddleware(createEpicMiddleware(rootEpic))
)

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {configureStore} from './store.js'
import {Provider} from 'react-redux'

const store = configureStore()

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

App.js

import React, { Component } from 'react';
import {connect} from 'react-redux'
import {newNotification} from './store.js'

class App extends Component {

  render() {
    return (
      <div className="App">
        {this.props.notificationExistance ? (<p>{this.props.notificationMessage}</p>) : ''}
        <button onClick={this.props.onNotificationRequest}>Click!</button>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    notificationExistance : state.length > 0,
    notificationMessage : state
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onNotificationRequest: () => dispatch(newNotification(new Date().toDateString()))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

解决这个问题的关键代码就像你看到的一样简单,唯一与其他答案不同的是rootpic函数。

点1。与saga一样,为了获得一个顶级函数来接收动作流并返回动作流,您必须将这些epics组合在一起,因此可以将它与中间件工厂createEpicMiddleware一起使用。在我们的例子中,我们只需要一个,所以我们只有rootEpic,所以我们不需要组合任何东西,但知道事实是很好的。

点2。我们的rootEpic只需要5行代码就可以处理副作用,这太棒了!包括这几乎是声明性的事实!

点3。逐行解析(在注释中)

const rootEpic = (action$) => {
  // sets the incoming constant as a stream 
  // of actions with  type NEW_NOTIFICATION
  const incoming = action$.ofType(NEW_NOTIFICATION)
  // Merges the "incoming" stream with the stream resulting for each call
  // This functionality is similar to flatMap (or Promise.all in some way)
  // It creates a new stream with the values of incoming and 
  // the resulting values of the stream generated by the function passed
  // but it stops the merge when incoming gets a new value SO!,
  // in result: no quitNotification action is set in the resulting stream
  // in case there is a new alert
  const outgoing = incoming.switchMap((action) => {
    // creates of observable with the value passed 
    // (a stream with only one node)
    return Observable.of(quitNotification())
      // it waits before sending the nodes 
      // from the Observable.of(...) statement
      .delay(NOTIFICATION_TIMEOUT)
  });
  // we return the resulting stream
  return outgoing;
}

我希望这能有所帮助!

其他回答

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等异步操作的指南。

在尝试了各种流行的方法(动作创造者,坦克,传奇,史诗,效果,自定义中间件)后,我仍然觉得可能还有改进的空间,所以我在这篇博客文章中记录了我的旅程,在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现场的例子,以及完整的例子。我还在继续写文档和例子。

我很想听听你的反馈。

这很简单。使用trim-redux包,在componentDidMount或其他地方这样写,并在componentWillUnmount中杀死它。

componentDidMount() {
  this.tm = setTimeout(function() {
    setStore({ age: 20 });
  }, 3000);
}

componentWillUnmount() {
  clearTimeout(this.tm);
}

我知道这个问题有点老了,但我将介绍另一种解决方案,使用还原可观测。史诗。

引用官方文件:

什么是可还原可观察?

基于RxJS 5的Redux中间件。合成和取消异步操作 创造副作用等等。

史诗是还原可观察的核心原语。

它是一个接受操作流并返回流的函数 的行动。行动进,行动出。

简而言之,您可以创建一个通过流接收操作的函数,然后返回一个新的操作流(使用常见的副作用,如超时、延迟、间隔和请求)。

让我发布代码,然后再详细解释一下

store.js

import {createStore, applyMiddleware} from 'redux'
import {createEpicMiddleware} from 'redux-observable'
import {Observable} from 'rxjs'
const NEW_NOTIFICATION = 'NEW_NOTIFICATION'
const QUIT_NOTIFICATION = 'QUIT_NOTIFICATION'
const NOTIFICATION_TIMEOUT = 2000

const initialState = ''
const rootReducer = (state = initialState, action) => {
  const {type, message} = action
  console.log(type)
  switch(type) {
    case NEW_NOTIFICATION:
      return message
    break
    case QUIT_NOTIFICATION:
      return initialState
    break
  }

  return state
}

const rootEpic = (action$) => {
  const incoming = action$.ofType(NEW_NOTIFICATION)
  const outgoing = incoming.switchMap((action) => {
    return Observable.of(quitNotification())
      .delay(NOTIFICATION_TIMEOUT)
      //.takeUntil(action$.ofType(NEW_NOTIFICATION))
  });

  return outgoing;
}

export function newNotification(message) {
  return ({type: NEW_NOTIFICATION, message})
}
export function quitNotification(message) {
  return ({type: QUIT_NOTIFICATION, message});
}

export const configureStore = () => createStore(
  rootReducer,
  applyMiddleware(createEpicMiddleware(rootEpic))
)

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {configureStore} from './store.js'
import {Provider} from 'react-redux'

const store = configureStore()

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

App.js

import React, { Component } from 'react';
import {connect} from 'react-redux'
import {newNotification} from './store.js'

class App extends Component {

  render() {
    return (
      <div className="App">
        {this.props.notificationExistance ? (<p>{this.props.notificationMessage}</p>) : ''}
        <button onClick={this.props.onNotificationRequest}>Click!</button>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    notificationExistance : state.length > 0,
    notificationMessage : state
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onNotificationRequest: () => dispatch(newNotification(new Date().toDateString()))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

解决这个问题的关键代码就像你看到的一样简单,唯一与其他答案不同的是rootpic函数。

点1。与saga一样,为了获得一个顶级函数来接收动作流并返回动作流,您必须将这些epics组合在一起,因此可以将它与中间件工厂createEpicMiddleware一起使用。在我们的例子中,我们只需要一个,所以我们只有rootEpic,所以我们不需要组合任何东西,但知道事实是很好的。

点2。我们的rootEpic只需要5行代码就可以处理副作用,这太棒了!包括这几乎是声明性的事实!

点3。逐行解析(在注释中)

const rootEpic = (action$) => {
  // sets the incoming constant as a stream 
  // of actions with  type NEW_NOTIFICATION
  const incoming = action$.ofType(NEW_NOTIFICATION)
  // Merges the "incoming" stream with the stream resulting for each call
  // This functionality is similar to flatMap (or Promise.all in some way)
  // It creates a new stream with the values of incoming and 
  // the resulting values of the stream generated by the function passed
  // but it stops the merge when incoming gets a new value SO!,
  // in result: no quitNotification action is set in the resulting stream
  // in case there is a new alert
  const outgoing = incoming.switchMap((action) => {
    // creates of observable with the value passed 
    // (a stream with only one node)
    return Observable.of(quitNotification())
      // it waits before sending the nodes 
      // from the Observable.of(...) statement
      .delay(NOTIFICATION_TIMEOUT)
  });
  // we return the resulting stream
  return outgoing;
}

我希望这能有所帮助!