An alternative side effect model for Redux apps

Overview

Redux Logo Landscape

redux-saga

npm version CDNJS npm Build Status Join the chat at https://gitter.im/yelouafi/redux-saga OpenCollective OpenCollective

redux-saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.

The mental model is that a saga is like a separate thread in your application that's solely responsible for side effects. redux-saga is a redux middleware, which means this thread can be started, paused and cancelled from the main application with normal redux actions, it has access to the full redux application state and it can dispatch redux actions as well.

It uses an ES6 feature called Generators to make those asynchronous flows easy to read, write and test. (if you're not familiar with them here are some introductory links) By doing so, these asynchronous flows look like your standard synchronous JavaScript code. (kind of like async/await, but generators have a few more awesome features we need)

You might've used redux-thunk before to handle your data fetching. Contrary to redux thunk, you don't end up in callback hell, you can test your asynchronous flows easily and your actions stay pure.

Getting started

Install

$ npm install redux-saga

or

$ yarn add redux-saga

Alternatively, you may use the provided UMD builds directly in the <script> tag of an HTML page. See this section.

Usage Example

Suppose we have a UI to fetch some user data from a remote server when a button is clicked. (For brevity, we'll just show the action triggering code.)

class UserComponent extends React.Component {
  ...
  onSomeButtonClicked() {
    const { userId, dispatch } = this.props
    dispatch({type: 'USER_FETCH_REQUESTED', payload: {userId}})
  }
  ...
}

The Component dispatches a plain Object action to the Store. We'll create a Saga that watches for all USER_FETCH_REQUESTED actions and triggers an API call to fetch the user data.

sagas.js

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'

// worker Saga: will be fired on USER_FETCH_REQUESTED actions
function* fetchUser(action) {
   try {
      const user = yield call(Api.fetchUser, action.payload.userId);
      yield put({type: "USER_FETCH_SUCCEEDED", user: user});
   } catch (e) {
      yield put({type: "USER_FETCH_FAILED", message: e.message});
   }
}

/*
  Starts fetchUser on each dispatched `USER_FETCH_REQUESTED` action.
  Allows concurrent fetches of user.
*/
function* mySaga() {
  yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}

/*
  Alternatively you may use takeLatest.

  Does not allow concurrent fetches of user. If "USER_FETCH_REQUESTED" gets
  dispatched while a fetch is already pending, that pending fetch is cancelled
  and only the latest one will be run.
*/
function* mySaga() {
  yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
}

export default mySaga;

To run our Saga, we'll have to connect it to the Redux Store using the redux-saga middleware.

main.js

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'

import reducer from './reducers'
import mySaga from './sagas'

// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

// then run the saga
sagaMiddleware.run(mySaga)

// render the application

Documentation

Translation

Using umd build in the browser

There is also a umd build of redux-saga available in the dist/ folder. When using the umd build redux-saga is available as ReduxSaga in the window object. This enables you to create Saga middleware without using ES6 import syntax like this:

var sagaMiddleware = ReduxSaga.default()

The umd version is useful if you don't use Webpack or Browserify. You can access it directly from unpkg.

The following builds are available:

Important! If the browser you are targeting doesn't support ES2015 generators, you must transpile them (i.e. with babel plugin) and provide a valid runtime, such as the one here. The runtime must be imported before redux-saga:

import 'regenerator-runtime/runtime'
// then
import sagaMiddleware from 'redux-saga'

Building examples from sources

$ git clone https://github.com/redux-saga/redux-saga.git
$ cd redux-saga
$ yarn
$ npm test

Below are the examples ported (so far) from the Redux repos.

Counter examples

There are three counter examples.

counter-vanilla

Demo using vanilla JavaScript and UMD builds. All source is inlined in index.html.

To launch the example, open index.html in your browser.

Important: your browser must support Generators. Latest versions of Chrome/Firefox/Edge are suitable.

counter

Demo using webpack and high-level API takeEvery.

$ npm run counter

# test sample for the generator
$ npm run test-counter

cancellable-counter

Demo using low-level API to demonstrate task cancellation.

$ npm run cancellable-counter

Shopping Cart example

$ npm run shop

# test sample for the generator
$ npm run test-shop

async example

$ npm run async

# test sample for the generators
$ npm run test-async

real-world example (with webpack hot reloading)

$ npm run real-world

# sorry, no tests yet

TypeScript

Redux-Saga with TypeScript requires DOM.Iterable or ES2015.Iterable. If your target is ES6, you are likely already set, however, for ES5, you will need to add it yourself. Check your tsconfig.json file, and the official compiler options documentation.

Logo

You can find the official Redux-Saga logo with different flavors in the logo directory.

Redux Saga chooses generators over async/await

A few issues have been raised asking whether Redux saga plans to use async/await syntax instead of generators.

We will continue to use generators. The primary mechanism of async/await is Promises and it is very difficult to retain the scheduling simplicity and semantics of existing Saga concepts using Promises. async/await simply don't allow for certain things - like i.e. cancellation. With generators we have full power over how & when effects are executed.

Backers

Support us with a monthly donation and help us continue our activities. [Become a backer]

Sponsors

Become a sponsor and get your logo on our README on Github with a link to your site. [Become a sponsor]

License

Copyright (c) 2015 Yassine Elouafi.

Licensed under The MIT License (MIT).

Comments
  •  docs need a complete rework

    docs need a complete rework

    Actual documentation was mainly intended to showcase the library capabilities and how it is different from other middlewares. I think docs need to be reworked with a more 'conventional' approach

    My initial list

    1- API reference 2- Step by step tutorials 3- Section on unit testing Sagas (maybe also integration testing with store/middleware) 4- Examples on how to manage common concurrency patterns (watch and fork, watch and fork latest...) 5- Example solutions for some common flows (authorization, infinite scroll ...) 6- Links to external resources (good tutorials on Generators, redux-saga articles, app examples)

    Docs will use Gitbook format

    Any further ideas ?

    docs 
    opened by yelouafi 64
  • Saga -> Component communication (aka sagas and setState)

    Saga -> Component communication (aka sagas and setState)

    I want to set form loading state (spinner icon, disable input) when the user submits the form, and clear that state when the action completes or fails. Currently I am storing this state in my store, which involves creating an action creator and reducer, which is annoying for a few reasons:

    1. It's a lot more work than simply calling this.setState
    2. It's more difficult to reason about
    3. I don't really want to store local component state in my store

    Essentially I want to do this from within my form component:

    handleFormSubmit() {
      this.setState({ isSaving: true })
      this.props.dispatchSaveForm({ formData: this.props.formData })
        .finally(() => this.setState({ isSaving: false }))
    }
    
    enhancement help wanted discussion 
    opened by brettstack 57
  • Bundling with Rollup fails due to missing export

    Bundling with Rollup fails due to missing export

    Hello,

    i'm trying to bundle my project using redux-saga and stumbled upon an issue.

    using the node-resolve plugin for rollup and the { jsnext: true } option gives me the following error:

    'CHANNEL_END' is not exported by '$PATH_TO_PROJECT$\node_modules\redux-saga\es\internal\io.js' (imported by '$PATH_TO_PROJECT$\node_modules\redux-saga\es\utils.js') Error: 'CHANNEL_END' is not exported by '$PATH_TO_PROJECT$\node_modules\redux-saga\es\internal\io.js' (imported by '$PATH_TO_PROJECT$\node_modules\redux-saga\es\utils.js') at Module.traceExport ($PATH_TO_PROJECT$\node_modules\rollup\src\Module.js:376:17) ...

    Looking at the es/internal/io.js module there is no export for this but the es/utils.js re-exports this constant.

    es/utils.js export { CHANNEL_END, asEffect } from './internal/io';

    Setting the { jsnext: false } option solves the issue as a workaround but also disables all other es6 imports.

    Did I miss something or is this a small bug?

    Thanks in advance.

    bug 
    opened by zech 48
  • Server Side Rendering (Universal)

    Server Side Rendering (Universal)

    Do sagas handle server side rendering?

    When on the server, there is a need to adapt async code into a synchronous flow, as we are handling a blocking server request that is waiting on some sort of data. If we're using React, we need to call ReactDOM.renderToString after we have our redux state populated. Often times that state is sourced from async sources, such as a REST API. So, we have to resolve those asynchronous processes back in the flow of the request.

    I currently use Promises (and some custom data fetching dectorators) to handle this:

    Promise.all(
      prefetchData(components, store)
    ).then(render);
    

    That's a simplified form, but it's basically the same thing. My component decorator defines certain actions to send through Redux when calling prefetchData on it. It gets the promises from those actions (I use redux-promise currently) and bundles them up with Promise.all to let them be resolved before rendering.

    Now, given a relatively complex saga, it looks like there isn't a sole promise to resolve. Is there, instead, some way we could watch for the completion of a set of sagas? That is, I could continue to dispatch my actions from my component decorators, followed by some call to the saga runner to wait for the generator functions we've invoked from those actions to finish. If so, I can use that as a means to get back to my synchronous flow and return a response to my blocking server request.

    examples 
    opened by timdorr 48
  • Question: Authentication flow

    Question: Authentication flow

    I'm trying to implement user authentication with following requirements:

    • Sign-in can be triggered by user action
    • Auth token must be refreshed after some delay upon successful sign-in
    • If there is token stored in localStorage on app start then we must refresh it immediately

    Here's my attempt:

    function* authentication() {
      let refreshDelay = null;
    
      let token = JSON.parse(localStorage.getItem('authToken'));
    
      if (token) 
        refreshDelay = call(delay, 0);  // instant refresh
    
      while (true) {
        const {action} = yield race({
          action: take([SIGN_IN, SIGN_OUT]),
          delay: refreshDelay || call(waitForever)
        });
        refreshDelay = null;
    
        if (action && action.type === SIGN_OUT) {
          localStorage.removeItem('authToken');
          continue;
        }
    
        try {
          token = yield action == null ? auth.refreshToken(token) : auth.signIn();
          localStorage.setItem('authToken', JSON.stringify(token));
          yield put(authSuccess(token));
          refreshDelay = call(delay, token.expires_in);
        } catch (e) {
          localStorage.removeItem('authToken');
          yield put(authFailure(e));
        }
      }
    }
    

    This code works well, but I wonder if there is more elegant solution for handling refresh. Currently it's too difficult to track refreshDelay effect. I thought about extracting refresh to separate saga that waits for AUTH_SUCCESS action and then sets up a delay, but in this case I won't be able to cancel scheduled refresh if e.g. user signs out.

    enhancement 
    opened by aikoven 46
  • [API Proposal] introducing END

    [API Proposal] introducing END

    Besides usage with channels (see #254), END can also be used with store actions. This would allow us to break the while(true) loop inside watchers. this combined with #78 (support for attached forks) would allow us to write universal Saga code.

    END is a special action, when there is saga waiting on a take

    function* saga() {
      const action = take(SOME_ACTION)
      //...
    }
    

    if END is dispatched, then the take will be resolved no matter what SOME_ACTION is. Similarly, if the store/channel already ENDed and a Saga emits a take(SOME_ACTION) it'll also be resolved immediately with END (see Semantics section for the why).

    I was planning on implementing this on the real-world example but due to lack of time i'll give a simpler example here

    function* clientOnlySaga() {
      let action = yield take(CLIENT_ONLY_ACTION)
      while(action !== END) {
        yield fork(myClientTask)
        action = yield take(CLIENT_ONLY_ACTION)
      }
    }
    
    function* universalSaga() {
      let action = yield take(UNIVERSAL_ACTION)
      while(action !== END) {
        yield fork(myUniversalTask)
        action = yield take(UNIVERSAL_ACTION)
      }
    }
    
    function* rootSaga() {
      yield [
        fork(clientOnlySaga),
        fork(universalSaga)
      ]
    }
    
    // store/middleware setup
    const sagaMiddleware = createSagaMiddleware()
    const store = createStore(
      rootReducer,
      applyMiddleware(sagaMiddleware)
    )
    const rootTask = sagaMiddleware.run(rootSaga)
    

    If we run the code i the client, then it'll be as with while(true) because no END action is dispatched on the client. On the server however, using for example React Router

    match({routes, location: req.url}, (error, redirectLocation, renderProps) => {
      if (error) { ... } 
      else if (redirectLocation) { ... } 
      else if (renderProps && renderProps.components) {
          const rootTask = sagaMiddleware.run(rootSaga)
    
          // this will cause the universal Saga tasks to trigger
          renderToString(
             <Root store={store} renderProps={renderProps} type="server"/>
          )
          // notify Saga that there will be no more dispatches
          // this will break the while loop of watchers
          store.dispatch(END)
          rootTask.done.then(() => {
            res.status(200).send(
              Layout(
                renderToString(
                  <Root store={store} renderProps={renderProps} type="server"/>
                ),
                JSON.stringify(store.getState())
              )
            )
          }).catch(...)
    
        } else {
          res.status(404).send('Not found')
        }
      })
    })
    

    Above dispatching END will cause the while loops to break and the related saga to terminate its main body. With support for attached forks, a parent which has terminated its own body will wait for all forked children (attached by default) to terminate before returning. So the root saga will terminate after all fired tasks terminate.

    There is on drawback though: we need to render twice: the 1st time to trigger the necessary actions and fire the load tasks, and the 2nd to send the final result to the client; but I dont think it's a big deal, because the time wasted on rendering would be non significant here compared to the latency of network request. And more importantly we can run the same code on the client and server.

    Semantics of END

    The motivation for the above behavior arises from the need to define precise semantics for END esp. how it should compose within race and parallel effects.

    For some time, I was confused because I looked to END from the Rx's point of view: i.e. as an end of a stream. But Actually there is no notion of stream in redux-saga, there is only notion of Futures (e.g. Promises): take(action), call(func), join(task) ... can all be viewed like normal function calls which return Futures (like await in async functions). So the issue become how do we translate END of a streams into the Future/Promise model.

    IMO the answer is the Never-happening-Future. For example suppose we have a kind of nextEvent method which returns the next event occurrence on a stream of events. What happens if we call nextEvent on a stream that has already terminated

    myFunc() {
      const promise = nextEvent(stream)
      // ...
    }
    

    Since the stream is terminated, the promise should never resolve because there is no more future actions, so myFunc won't make any progress.

    Once we define it this way, the sense of combining END with race and parallel becomes more obvious. We have a simple and precise algebra. End behaves like a sort of a Zero for Futures.

    // a race with never will always yields to the other Future
    Never `race` Future = Future
    
    // a parallel  having a Never will always yield Never
    Never `parallel` Future = Never
    

    This is how it's actually implemented in the proposal.

    The doubt I'm having though is whether we should expose END explicitly to the developer or if we should handle it automatically by terminating the Saga. In the above example we used explicit handling of END. Now with automatic handling we can write

    function* saga() {
      while(true) {
        yield take(action)
        yield fork(task)
      }
    }
    
    function* parentSaga() {
      yield call(saga)
    }
    

    Above there is no explicit END value; if the take resolves with an END. then we can choose to terminate the Saga automatically and resolve its return value with END. the END would then propagate to the parent, so it'll be also terminated. The only way to escape from END will be inside forked tasks and within race effects.

    Automatic handling prevents us from dealing manually with END results. OTOH manual handling of END gives more flexibility (for example starting a second stream after a 1st one terminates)

    enhancement feedback wanted discussion 
    opened by yelouafi 43
  • Testing is Done Right

    Testing is Done Right

    Hi,

    My comment is about: https://redux-saga.github.io/redux-saga/docs/advanced/Testing.html

    I think the way you see testing sagas is wrong and puts emphasis on exact 'implementation', and not 'result'.

    The way you do tests: Check the call for each 'yield', and muck a return value to that yield. In this case if I change the implementation of my saga (in such a way that doesn't affect the results), I need to change me tests. Any refactoring of code will necessarily mean changing the tests.

    My view is that your tests check that you implemented your saga in a very very specific way and not that the saga is doing what it needs to be doing.

    The way we write test for our sagas is:

    1. Set up a test store with a specific scenario. Test store includes a reducer that interacts with actions that are fired by the saga.
    2. Run the saga using the standard redux-saga framework.
    3. See the side effects (actions that were sent).

    I think this way of testing puts the emphasis where it needs to be. If we refactor the saga, and it still does exactly what it needs to do, the tests won't need to change (making the tests helpful for refactoring).

    WDYT?

    enhancement 
    opened by 3LOK 42
  • how to throttle and then watchLatest, can we compose high-level helpers?

    how to throttle and then watchLatest, can we compose high-level helpers?

    Taking example from https://github.com/yelouafi/redux-saga/issues/70#issuecomment-182475883

    function* fetchUser(action) {
       try {
          const user = yield call(Api.fetchUser,action.payload.userId);
          yield put({type: "USER_FETCH_SUCCEEDED", user: user});
       } catch (e) {
          yield put({type: "USER_FETCH_FAILED",message: e.message});
       }
    }
    
    function* mySaga() {
      yield watchLatest("USER_FETCH_REQUESTED",fetchUser);
    }
    

    Now imagine that if 2 USER_FETCH_REQUESTED gets dispatched very close to each others, instead of firing 2 requests, and then cancelling the first one, throttling would permit to avoid doing the 1st request in the first place.

    I don't have a real usecase for this but as we are going to implement high-level apis (so probably throttle?), can it be possible to compose them?

    enhancement 
    opened by slorber 39
  • Is there a better way to do takeFirst?

    Is there a better way to do takeFirst?

    So the takeEvery and takeLatest helper functions are quite useful. But in a situation where you want to take the first take, but ignore the rest, such as a user clicking the delete button multiple times, what is the best way to write your saga watcher function so that it only takes the first delete action dispatch?

    By modifying takeLatest I got a function that looks like this:

    function* takeFirst(pattern, saga, ...args) {
      const task = yield fork(function* () {
        let firstTask = true;
        while(true) {
          const action = yield take(pattern);
          if (firstTask) {
            firstTask = false;
            yield call(saga, ...args.concat(action));
            firstTask = true;
          }
        }
      });
      return task;
    }
    

    Now in a watcher you might be able to do this:

    export function* watchDeleteWidgetsSaga() {
      yield takeFirst(DELETE_WIDGETS_SAGA, deleteAndUpdateWidgets);
    }
    

    Is this a good way to "takeFirst"? Or are there already saga helpers and effect creators in the existing saga api that would help me do this? Or is "takeFirst" just an anti-pattern completely - if so why?

    question 
    opened by ekeric13 36
  • How to put() within a callback?

    How to put() within a callback?

    There have been a few instances where I've wanted to put within a callback to realize that the action never gets reduced on. Is it possible to do the following with sagas?

    function* hideNotification() {
      let clearTimer = null;
      const { SET_NOTIFICATION, CLEAR_NOTIFICATION } = NotificationActions.actionTypes;
    
      while(true) {
        const action = yield take([SET_NOTIFICATION, CLEAR_NOTIFICATION]);
        if (clearTimer) {
          clearTimeout(clearTimer);
          clearTimer = null; 
        }
    
        if (action.type === SET_NOTIFICATION) {
          clearTimer = setTimeout(() => put(NotificationActions.clearNotification()), 300);
        }
      }
    }
    
    question 
    opened by bgerm 36
  • [Typescript] Yield return value type safety

    [Typescript] Yield return value type safety

    I'm ramping up on redux-saga and Typescript by working on a small project.

    Something I've noticed is that the return value of yield effect(...) is always any. I'm not familiar enough with the platform to understand if this is a limitation of Typescript, or some missing type annotation in redux-saga/my code.

    E.g.:

    function selector(state: State) {
        return state.selected;
    }
    
    function* yieldNumber() {
        yield 1;
    }
    
    function* saga() {
        let num = yield call(yieldNumber); // num has inferred type of any
        let selected = yield select(selector); // selected: any
    
        // My workaround
        let num: number = yield call(yieldNumber); // works as long as I don't misrepresent the type
    }
    
    opened by ssynix 35
  • chore: run prettier on all files

    chore: run prettier on all files

    | Q | A | | ----------------------- | ---------------------------------------------------------------------- | | Fixed Issues? | | | Patch: Bug Fix? | | | Major: Breaking Change? | | | Minor: New Feature? | | | Tests Added + Pass? | Yes | | Any Dependency Changes? | |

    We have quite a few files checked into code that haven't had prettier run on them. We have a pre-commit rule to run prettier on everything that was modified, but it looks like we didn't run prettier initially to format all the files.

    opened by neurosnap 2
  • feat: throttle a channel

    feat: throttle a channel

    Closes: #1805

    This allows a pattern or channel to be passed to throttle. It's a feature we've never had before but it's also something that is "supported" in our docs.

    | Q | A | | ----------------------- | ---------------------------------------------------------------------- | | Fixed Issues? | #1805 | | Patch: Bug Fix? | | | Major: Breaking Change? | | | Minor: New Feature? | Yes | | Tests Added + Pass? | Yes | | Any Dependency Changes? | |

    opened by neurosnap 2
  • How to know when the task queue is empty

    How to know when the task queue is empty

    I am attempting to write some dev tools for redux-saga that use the SagaMonitor interface. I have helpers for tracking what effects are currently pending/resolved/rejected etc.

    One thing I am struggling with is how to know when all queued effects have been processed. Is there a signal or piece of state I can read to know this?

    For example, say we are running the below saga

    function * myTask() {
        while(true) {
            const action = yield take('SOME_ACTION');
            if (action.payload.value === 1) {
                yield put({ type: 'SOME_OTHER_ACTION' });
            }
        }
    }
    

    At some point, all effects have been triggered and/or resolved and we are in a steady state waiting for the next action. The queue is empty so to speak (this may not be the correct nomenclature).

    How do I know when I'm in that state, would the next tick be the correct point to mark this assertion?

    opened by madcapnmckay 1
  • TS Error: Return type annotation circularly references itself.

    TS Error: Return type annotation circularly references itself.

    Upgrading from v 0.14.8 to 1.1.3 (any v1+ leads to the same issue) After building in dev env, I am getting an typescript error from the library

    [at-loader] ./node_modules/@redux-saga/core/types/ts3.6/effects.d.ts:465:4 TS2577: Return type annotation circularly references itself. The project does compile but sagas do not work

    I have another project with the exact same typescript / babel version which compiles fine. "@babel/core": "7.2.2", "typescript": "3.8.3",

    Any assistance will be appreciated.

    bug question 
    opened by Exrun94 1
  • Snippet

    Snippet

    | Q | A | | ----------------------- | ---------------------------------------------------------------------- | | Fixed Issues? | Fixes #1, Fixes #2 | | Patch: Bug Fix? | | | Major: Breaking Change? | | | Minor: New Feature? | | | Tests Added + Pass? | Yes | | Any Dependency Changes? | |

    To whom it may concern,

    I have add a new snippet 4 besides the snippet 3 in the Example Usage from the main page. If we can improve the docs with the optimised version, redux-saga could be more user-friendly and cause less confusion for future developers.

    You can ignore the configureStore branch and merge this branch please.

    If the code is no problem, and I would be happy to add it along the the rest createStore docs gradually.

    Cheers, David

    docs 
    opened by LordMoMA 3
  • Unit test for api polling done on Redux-saga side

    Unit test for api polling done on Redux-saga side

    Description

    I am polling an API and using similar code as given in this code pen I am trying to test the saga part of it using Jest but I am confused how to test a generator function with a delay and while loop. In the codepen the delay is a function:

    const delay = (ms) => new Promise((res) => setTimeout(res, ms));
    

    Can someone please help here?

    opened by soumyaa1804 3
  • [email protected](Aug 20, 2022)

  • [email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • @redux-saga/[email protected](Aug 20, 2022)

  • [email protected](Aug 13, 2022)

    Minor Changes

    • #2295 bed4458 Thanks @lourd! - Adds type inference for result of task returned from runSaga and SagaMiddleware.run

    • #2296 612cae8 Thanks @lourd! - Updates Channel type to eliminate void-emitting pitfall

    • #2308 8207e33 Thanks @Andarist, @neurosnap! - exports field has been added to the package.json manifest. It limits what files can be imported from a package but we've tried our best to allow importing all the files that were considered to be a part of the public API.

      This should fix the compatibility with Node.js ESM support.

    Patch Changes

    • #2261 5ae6578 Thanks @neurosnap! - Require CpsCallback in all functions passed to the cps effect creator. This fixes a regression caused by TS 4.0 changing the behavior around spreading never into tuple types

    • #2004 20f22a8 Thanks @gilbsgilbs! - A generic type has been added to the Task interface and that should be preferred over using a generic parameter in Task#result and Task#toPromise.

    • #2068 586179c Thanks @mikabytes! - Added warnings when using take(channelOrPattern) incorrectly with more than one parameter. It helps to surface problem with take(ACTION_A, ACTION_B) being used instead of take([ACTION_A, ACTION_B]).

    • Updated dependencies [bed4458, 612cae8, 5ae6578, 979b8b4, 20f22a8, 586179c]:

    Source code(tar.gz)
    Source code(zip)
  • @redux-saga/[email protected](Aug 13, 2022)

    Minor Changes

    • #2295 bed4458 Thanks @lourd! - Adds type inference for result of task returned from runSaga and SagaMiddleware.run

    • #2296 612cae8 Thanks @lourd! - Updates Channel type to eliminate void-emitting pitfall

    Patch Changes

    • #2004 20f22a8 Thanks @gilbsgilbs! - A generic type has been added to the Task interface and that should be preferred over using a generic parameter in Task#result and Task#toPromise.

    • #2270 d2579a2 Thanks @Methuselah96! - Inlined Redux Action type to fix compatibility with strict package managers.

    Source code(tar.gz)
    Source code(zip)
  • @redux-saga/[email protected](Aug 13, 2022)

  • @redux-saga/[email protected](Aug 13, 2022)

    Minor Changes

    • #2308 8207e33 Thanks @Andarist, @neurosnap! - exports field has been added to the package.json manifest. It limits what files can be imported from a package but we've tried our best to allow importing all the files that were considered to be a part of the public API.

      This should fix the compatibility with Node.js ESM support.

    Patch Changes

    • #2293 2d2214e Thanks @neurosnap! - Fixed an issue with arguments that exceed the maximum value for the internally-used setTimeout. Previously it could overflow based on the input that was too big and thus a timeout could resolve immediately.
    Source code(tar.gz)
    Source code(zip)
  • @redux-saga/[email protected](Aug 13, 2022)

    Minor Changes

    • #2308 8207e33 Thanks @Andarist, @neurosnap! - exports field has been added to the package.json manifest. It limits what files can be imported from a package but we've tried our best to allow importing all the files that were considered to be a part of the public API.

      This should fix the compatibility with Node.js ESM support.

    Source code(tar.gz)
    Source code(zip)
  • @redux-saga/[email protected](Aug 13, 2022)

    Minor Changes

    • #2295 bed4458 Thanks @lourd! - Adds type inference for result of task returned from runSaga and SagaMiddleware.run

    • #2296 612cae8 Thanks @lourd! - Updates Channel type to eliminate void-emitting pitfall

    • #2308 8207e33 Thanks @Andarist, @neurosnap! - exports field has been added to the package.json manifest. It limits what files can be imported from a package but we've tried our best to allow importing all the files that were considered to be a part of the public API.

      This should fix the compatibility with Node.js ESM support.

    Patch Changes

    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Sep 17, 2019)

  • v1.1.0(Sep 17, 2019)

  • v1.0.0(Jan 20, 2019)

    Breaking changes:

    • channel and actionChannel have default buffer of buffers.expanding()
    • errors thrown during put execution are no longer caught and swallowed, you need to catch them manually
    • signature of join & cancel, they both accept now a single task descriptor or an array of those (previously they have accepted variadic length of arguments)
    • removed some deprecated APIs - takeEvery, takeLatest, throttle from the redux-saga entry point (they are and were importable from redux-saga/effects), takem, put.sync and executing array of effects, there is explicit API for this for already some time - all effect
    • changed API of runSaga - it no longer accepts subscribe option, you should create a channel (preferably stdChannel), pass it as channel argument to the runSaga API and communicate with through it with take and put methods
    • refactored shape of the effect objects to { [IO]: true, type, payload }
    • removed asEffect, effect types are public, effect shapes are stable, so if for whatever reason you want to do some effect inspection you can just check them without us providing additional helpers for it
    • removed logError, use only onError option (it's signature is onError(error, { sagaStack }))
    • END will now finish the race effects
    • task.done getter was changed to be task.toPromise method
    • channels private getters (__takers__ and __closed__) got removed
    • delay became an effect
    • redux-saga/utils got removed, exports available exclusively there got moved to separate packages - @redux-saga/deferred, @redux-saga/delay-p, @redux-saga/is, @redux-saga/symbols & @redux-saga/testing-utils
    • eventChannel does no longer accept matcher argument - it was a hacky way to support previous implementation of stdChannel
    • {effects, utils} can't imported from 'redux-saga' anymore
    • most runtime type checks got hidden behing development checks, inputs might not be validated in production (failed validation resulted in error being thrown anyway)
    • detach helper returns a new effect instead of mutating the input one
    • we have stopped interpreting effects returned from fork, this shouldn't affect any real-life code, affected patterns look like this fork(() => effectCreator()) and fork(takeEvery, 'type', fn)
    • TS typings got revamped - they require [email protected]>3.1 now

    New:

    • babel-plugin-redux-saga - can be used to enhance stack traces of thrown errors
    • multicastChannel - no buffering, notify all pending takers, multicastChannel#take(cb, matcher = matchers.wildard)
    • support for yield take(multicastChannel, pattern)
    • internal stdChannel got reworked to be a singleton object (it is wrapped multicastChannel's instance'), also it is an exported API to support new runSaga's signature - this should also result in being a small perf boost
    • effectMiddlewares - useful especially for testing, you can intercept/hijack any effect and resolve it on your own - passing it very redux-style to the next middleware (last being redux-saga itself). How it might be used can be checked here.
    • takeLeading effect - it takes "leading" action and ignores all incoming ones of the same type while the "leading" is still handled (useful for things debouncing)
    • retry effect with the signature of retry(maxTries, delayLength, worker, ...args)
    • debounce effect with the signature of debounce(delayLength, pattern, worker, ...args)
    • new rootSagaStarted hook for saga monitor
    • added dev warning about dispatching frozen actions, for scheduling purposes we are using Object.defineProperty on actions dispatched with put effect, so we have to be able to mutate the action object
    • added dev warning about using async generators, they are not supported by redux-saga
    • CommonJS entries are proxied now. This means that process.env.NODE_ENV is read only once and based on that value the appropriate bundle (development or production) is loaded conditionally with require. process.env is slow in node.js so this should improve performance a little bit.
    • isRoot property on Task (you probably won't ever need to use it, it's internal)

    Fixes:

    • keeping single stdChannel in the internals allowed to fix 2 bugs with missed actions (see #707 and #1146)
    • onError should get called now even if you throw non-Errors in your code.
    • we suspend the scheduler before running a root saga, so it should behave the same as forked sagas in terms of scheduling
    • small memory leak for closed actionChannels
    • issue with joiner not ending in cancelled state when joining cancelled task
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-rc.0(Dec 21, 2018)

    This is first (and very likely last 😉) release candidate version 🎉. We expect no more breaking changes before releasing stable v1 (truth to be told there were not many breaking changes during whole beta process - the API is mostly the same).

    Added:

    • isRoot property on Task (you probably won't ever need to use it, it's internal)
    • dev warning for using delay effect incorrectly

    Changed:

    • sagaStack was refactored - onError call signature is now onError(error, { sagaStack })

    Removed:

    • removed logError, use only onError option
    • removed redux-saga/utils entirely, move of those are moved to separate packages

    Fixed:

    • issue with joiner not ending in cancelled state when joining cancelled task

    Improved:

    • many docs improvements
    • revamped TS typings (thanks to @aikoven)

    Many thanks once more to @restrry and @shinima who made it possible with their contributions.

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.3(Oct 17, 2018)

    This is rather a smaller release, but some things have changed or got improved.

    Added:

    • new rootSagaStarted hook for saga monitor

    Changed:

    • we reverted changing effect types to Symbols, this previous change has caused some problems and we came to conclusion that strings are "good enough" for us
    • detach helper returns a new effect instead of mutating the input one
    • we suspend the scheduler before running a root saga, so it should behave the same as forked sagas in terms of scheduling

    Removed:

    • asEffect, it shouldn't be used, effect types are public, effect shapes are stable, so if for whatever reason you want to do some effect inspection you can just check them without us providing additional helpers for it

    Fixed:

    • we have stopped interpreting effects returned from fork, this shouldn't affect any real-life code, affected patterns look like this fork(() => effectCreator()) and fork(takeEvery, 'type', fn)
    • small memory leak for closed actionChannels

    Polished:

    • added dev warning about dispatching frozen actions, for scheduling purposes we are using Object.defineProperty on actions dispatched with put effect, so we have to be able to mutate the action object
    • added dev warning about using async generators, they are not supported by redux-saga
    • we are reusing @babel/runtime helpers, this has helped us to remove some bytes from our bundles
    • CommonJS entries are proxied now. This means that process.env.NODE_ENV is read only once and based on that value the appropriate bundle (development or production) is loaded conditionally with require. process.env is slow in node.js so this should improve performance a little bit.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.2(Aug 26, 2018)

    This release is highly motivated by our 2 new core members @restrry & @shinima. They have put a lot of work into creating PRs & helping others with their issues.

    We've recently extracted some modular packages, you can see @redux-saga/deferred, @redux-saga/delay-p, @redux-saga/is & @redux-saga/symbols. At the same time we've managed to shave off some bytes from the core build - according to some tests of our we've managed remove over 1kb, but your YMMV ofc.

    Added:

    • retry effect with the signature of retry(maxTries, delayLength, worker, ...args)
    • debounce effect with the signature of debounce(delayLength, pattern, worker, ...args)

    Changed:

    • {effects, utils} aren't imported from 'redux-saga' anymore. import them from redux-saga/effects, redux-saga/utils
    • is helper should be imported from @redux-saga/is.
    • delay function (not effect!) should be imported from @redux-saga/delay-p
    • signature of join & cancel, they both accept now a single task descriptor or an array of those (previously they have accepted variadic length of arguments)
    • refactored shape of the effect objects to { [IO]: true, type, payload }, their structure should be treated as opaque anyway, so we hope this doesn't break anyone
    • END will now finish the race effects
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.1(May 7, 2018)

    Error handling

    Let's face it. Errors coming from redux-saga were unreadable and made finding a root cause of the problem really difficult. We really want to improve this situation and with this release (thanks to @restrry's amazing contribution) we start to log "saga stacks" along with original errors. This works similar to what React already does with its "component stacks". Just look at this one: 36414608-2ba4fb8e-1623-11e8-9fa9-0857a6fe63a0

    You can even enhance those stacks with file names and line numbers by using in development our new babel-plugin-redux-saga (also thanks to @restrry). Let us know if there are any issues with this or if we can make them even more useful!

    Also onError should get called now even if you throw non-Errors in your code.

    Beside that few things have changed, you can read more about them below.

    Added:

    • takeLeading effect - it takes "leading" action and ignores all incoming ones of the same type while the "leading" is still handled (useful for things debouncing)
    • [email protected] got in supported range (this will let npm dedupe redux in node_modules, keep in mind though that we only depend on compose from that package)

    Changed:

    • delay became an effect, you still can import delay util from redux-saga/utils though
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.0(Nov 5, 2017)

    I must say that this release probably wouldn't be possible without community sponsors - both organizations and individual backers.

    I'm not saying that money given by the community has helped, but it is a nice incentive to work and a token of appreciation. Somehow the fact alone that people are sending money towards us got me lately back on track, going, writing some code, pushing things forward towards v1 release.

    Note that the money is not what you have most valuable to offer for OSS projects - your time is way more valuable. If you want some feature to get implemented, bug to be fixed or just want to help - please reach out to a project's maintainer, maybe ask for pointers about code internals so you can start working easier. OSS is done by regular people like you and most projects' code is not magic, everything can be figured out and you can get comfortable with it.

    Maintaining a popular project (over 10k ⭐️⭐️⭐️!) is not an easy task to do. Truth to be told you do not even have to write code to get exhausted. Answering issues, replying to people's demands etc is time and energy consuming.

    I'd want to think that at least in those areas I've maintained the package well - nearly every question gets answered and it gets answered within days. I've helped people fix their broken code, I've suggested how their apps can be structured, how to solve particular problems.

    However GitHub is not really a place where (most of such) questions should be asked. Whenever you consider asking a question on any project's GitHub, please consider if it is the right place to ask. There are many places you can get help from the community, such as obviously StackOverflow. In terms of redux-saga additionally you can reach out to the community on our gitter channel and redux-saga reactiflux's channel.

    New:

    • multicastChannel - no buffering, notify all pending takers, multicastChannel#take(cb, matcher = matchers.wildard)
    • support for yield take(multicastChannel, pattern)
    • internal stdChannel got reworked to be a singleton object (it is wrapped multicastChannel's instance'), also it is an exported API to support new runSaga's signature - this should also result in being a small perf boost
    • effectMiddlewares - useful especially for testing, you can intercept/hijack any effect and resolve it on your own - passing it very redux-style to the next middleware (last being redux-saga itself). How it might be used can be checked here. Many thanks to @eloytoro for this feature

    Breaking changes:

    • channel and actionChannel have default buffer of buffers.expanding()
    • errors thrown during put execution are no longer caught and swallowed, you need to catch them manually
    • eventChannel does no longer accept matcher argument - it was a hacky way to support previous implementation of stdChannel
    • exported util of arrayOfDeffered got renamed to the correct arrayOfDeferred
    • internal util of sym tries to use Symbol if it's available, this mainly breaks effects' "shape" - types no longer are simple strings, although no code should make any assumptions about effects' shape anyway
    • removed some deprecated APIs - takeEvery, takeLatest, throttle from the redux-saga entry point (they are and were importable from redux-saga/effects), takem, put.sync and executing array of effects, there is explicit API for this for already some time - all effect
    • changed API of runSaga - it no longer accepts subscribe option, you should create a channel (preferably stdChannel), pass it as channel argument to the runSaga API and communicate with through it with take and put methods
    • most runtime type checks got hidden behing development checks, inputs might not be validated in production (failed validation resulted in error being thrown anyway)
    • task.done getter was changed to be task.toPromise method
    • channels private getters (__takers__ and __closed__) got removed

    Bug fixes:

    • keeping single stdChannel in the internals allowed to fix 2 bugs with missed actions (see #707 and #1146), cc @gajus

    Internals:

    • We have started to use [email protected], hopefully it won't break anything. I've investigated differences between outputs and everything seems to be ok, but you never know ;) This should result in a little bit smaller and a little bit more performant code.
    Source code(tar.gz)
    Source code(zip)
  • v0.16.0(Oct 16, 2017)

    • added detach helper - this is used internally now by spawn to mark fork effect as detached, got exported and might be used in userland, it's useful for creating i.e. detached takeEvery
    import { detach } from 'redux-saga'
    import { takeEvery } from 'redux-saga/effects'
    
    // ...
    
    yield detach(takeEvery(ACTION_A, mightThrowSaga))
    
    • typings for detach got added thanks to @aikoven
    • removed #__PURE__ annotations from the source code, adding them automatically with annotate-pure-calls babel's plugin
    • made sagaStack property non-enumerable (this is attached sometimes by redux-saga to the thrown Errors)
    • unified internally error logging
    • removed annoying "saga has been cancelled" logs, didn't find a valid use case for them
    Source code(tar.gz)
    Source code(zip)
  • v0.15.6(Oct 16, 2017)

  • v0.15.5(Oct 16, 2017)

    TS Typings

    • optional cancel property on CpsCallback (thanks to @dannsam)
    • support for non-strict effect combinators (thanks to @aikoven
    • type definitions for util's cloneableGenerator function (thanks to @zyml)
    Source code(tar.gz)
    Source code(zip)
  • v0.15.4(Jun 23, 2017)

  • v0.15.3(May 5, 2017)

  • v0.15.2(May 5, 2017)

    • fixed problem with yielding falsy values - normally all non-interpretable yields are just injected back to your saga (const two = yield 2), however yielding falsy values prevented yielding saga to continue
    Source code(tar.gz)
    Source code(zip)
Owner
Redux-Saga
Easing side effects management in Redux applications.
Redux-Saga
RxJS middleware for action side effects in Redux using "Epics"

RxJS-based middleware for Redux. Compose and cancel async actions to create side effects and more. https://redux-observable.js.org Install This has pe

redux-observable 7.8k Dec 1, 2022
Redux bindings for client-side search

redux-search Higher-order Redux library for searching collections of objects. Search algorithms powered by js-worker-search. Check out the live demo a

Brian Vaughn 1.4k Nov 14, 2022
Redux Tutorial - share my experience regarding redux, react-redux and redux-toolkit

Redux Tutorial 1. Introduction to Redux 1.1 What is Redux & why Redux? A small JS Library for managing medium/large amount of states globally in your

Anisul Islam 36 Dec 1, 2022
Skeleton React App configured with Redux store along with redux-thunk, redux persist and form validation using formik and yup

Getting Started with React-Redux App Some Configrations Needed You guys need to modify the baseUrl (path to your server) in the server.js file that is

Usama Sarfraz 11 Jul 10, 2022
A Higher Order Component using react-redux to keep form state in a Redux store

redux-form You build great forms, but do you know HOW users use your forms? Find out with Form Nerd! Professional analytics from the creator of Redux

Redux Form 12.6k Dec 1, 2022
redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.js state.

redux-immutable redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.js state. When Redux creat

Gajus Kuizinas 1.9k Nov 14, 2022
A chart monitor for Redux DevTools https://www.npmjs.com/package/redux-devtools-chart-monitor

Redux DevTools Chart Monitor This package was merged into redux-devtools monorepo. Please refer to that repository for the latest updates, issues and

Redux 293 Nov 13, 2022
A simple app for study react with redux, redux saga and typescript.

React com Redux, Redux-Saga e TypeScript. ?? Uma aplicação simple para entender o funcionamento do Redux e a melhor maneira de utiliza-lo junto com o

João Marcos Belanga 1 May 24, 2022
Redux - Create forms using Redux And React

Exercício de fixação Vamos criar formulários utilizando Redux! \o/ Antes de inic

Márcio Júnior 2 Jul 21, 2022
A lightweight state management library for react inspired by redux and react-redux

A lightweight state management library for react inspired by redux and react-redux

null 2 Sep 9, 2022
Official React bindings for Redux

React Redux Official React bindings for Redux. Performant and flexible. Installation Using Create React App The recommended way to start new apps with

Redux 22.5k Dec 7, 2022
DevTools for Redux with hot reloading, action replay, and customizable UI

Redux DevTools Developer Tools to power-up Redux development workflow or any other architecture which handles the state change (see integrations). It

Redux 13.2k Dec 2, 2022
Ruthlessly simple bindings to keep react-router and redux in sync

Project Deprecated This project is no longer maintained. For your Redux <-> Router syncing needs with React Router 4+, please see one of these librari

React Community 7.9k Dec 2, 2022
The official, opinionated, batteries-included toolset for efficient Redux development

Redux Toolkit The official, opinionated, batteries-included toolset for efficient Redux development (Formerly known as "Redux Starter Kit") Installati

Redux 8.8k Nov 30, 2022
Thunk middleware for Redux

Redux Thunk Thunk middleware for Redux. npm install redux-thunk yarn add redux-thunk Note on 2.x Update Most tutorials today assume that you're using

Redux 17.4k Dec 2, 2022
Logger for Redux

Logger for Redux Now maintained by LogRocket! LogRocket is a production Redux logging tool that lets you replay problems as if they happened in your o

null 5.7k Dec 1, 2022
Selector library for Redux

Reselect Simple “selector” library for Redux (and others) inspired by getters in NuclearJS, subscriptions in re-frame and this proposal from speedskat

Redux 18.8k Dec 1, 2022
Analytics middleware for Redux

redux-analytics Analytics middleware for Redux. $ npm install --save redux-analytics Want to customise your metadata further? Check out redux-tap. Usa

Mark Dalgleish 490 Aug 5, 2022
:recycle: higher order reducer to add undo/redo functionality to redux state containers

redux undo/redo simple undo/redo functionality for redux state containers Protip: Check out the todos-with-undo example or the redux-undo-boilerplate

Daniel Bugl 2.8k Dec 4, 2022