Movatterモバイル変換


[0]ホーム

URL:


Skip to main content
🇺🇦 STOP WAR IN UKRAINE 🇺🇦

API Reference

Middleware API

createSagaMiddleware(options)

Creates a Redux middleware and connects the Sagas to the Redux Store

  • options: Object - A list of options to pass to the middleware. Currently supported options are:

    • context: Object - initial value of the saga's context.

    • sagaMonitor :SagaMonitor - If a Saga Monitor is provided, the middleware will deliver monitoring events to the monitor.

    • onError: (error: Error, { sagaStack: string }) - if provided, the middleware will call it with uncaught errors from Sagas. useful for sending uncaught exceptions to error tracking services.

    • effectMiddlewares : Function [] - allows you to intercept any effect, resolve it on your own and pass to the next middleware. Seethis section for a detailed example

    • channel: If provided, the middleware will use this channel instead of the defaultstdChannel() for

    • take andput effects.

Example

Below we will create a functionconfigureStore which will enhance the Store with a new methodrunSaga. Then in our main module, we will use the method to start the root Saga of the application.

configureStore.js

importcreateSagaMiddlewarefrom'redux-saga'
importreducerfrom'./path/to/reducer'

exportdefaultfunctionconfigureStore(initialState){
// Note: passing middleware as the last argument to createStore requires redux@>=3.1.0
const sagaMiddleware=createSagaMiddleware()
return{
...createStore(reducer, initialState,applyMiddleware(/* other middleware, */sagaMiddleware)),
runSaga: sagaMiddleware.run
}
}

main.js

importconfigureStorefrom'./configureStore'
importrootSagafrom'./sagas'
// ... other imports

const store=configureStore()
store.runSaga(rootSaga)

Notes

See below for more information on thesagaMiddleware.run method.

middleware.run(saga, ...args)

Dynamically runsaga. Can be used to run Sagasonly after theapplyMiddleware phase.

  • saga: Function: a Generator function
  • args: Array<any>: arguments to be provided tosaga

The method returns aTask descriptor.

Notes

saga must be a function which returns aGenerator Object. The middleware will then iterate over the Generator and execute all yielded Effects.

saga may also start other sagas using the various Effects provided by the library. The iteration process described below is also applied to all child sagas.

In the first iteration, the middleware invokes thenext() method to retrieve the next Effect. The middleware then executes the yielded Effect as specified by the Effects API below. Meanwhile, the Generator will be suspended until the effect execution terminates. Upon receiving the result of the execution, the middleware callsnext(result) on the Generator passing it the retrieved result as an argument. This process is repeated until the Generator terminates normally or by throwing some error.

If the execution results in an error (as specified by each Effect creator) then thethrow(error) method of the Generator is called instead. If the Generator function defines atry/catch surrounding the current yield instruction, then thecatch block will be invoked by the underlying Generator runtime. The runtime will also invoke any corresponding finally block.

In the case a Saga is cancelled (either manually or using the provided Effects), the middleware will invokereturn() method of the Generator. This will cause the Generator to skip directly to the finally block.

Effect creators

Notes:

  • Each function below returns a plain JavaScript object and does not perform any execution.
  • The execution is performed by the middleware during the Iteration process described above.
  • The middleware examines each Effect description and performs the appropriate action.

take(pattern)

Creates an Effect description that instructs the middleware to wait for a specified action on the Store.The Generator is suspended until an action that matchespattern is dispatched.

The result ofyield take(pattern) is an action object being dispatched.

pattern is interpreted using the following rules:

  • Iftake is called with no arguments or'*' all dispatched actions are matched (e.g.take() will match all actions)

  • If it is a function, the action is matched ifpattern(action) is true (e.g.take(action => action.entities) will match all actions having a (truthy)entities field.)

    Note: if the pattern function hastoString defined on it,action.type will be tested againstpattern.toString() instead. This is useful if you're using an action creator library like redux-act or redux-actions.

  • If it is a String, the action is matched ifaction.type === pattern (e.g.take(INCREMENT_ASYNC))

  • If it is an array, each item in the array is matched with aforementioned rules, so the mixed array of strings and function predicates is supported. The most common use case is an array of strings though, so thataction.type is matched against all items in the array (e.g.take([INCREMENT, DECREMENT]) and that would match either actions of typeINCREMENT orDECREMENT).

The middleware provides a special actionEND. If you dispatch the END action, then all Sagas blocked on a take Effect will be terminated regardless of the specified pattern. If the terminated Saga has still some forked tasks which are still running, it will wait for all the child tasks to terminate before terminating the Task.

takeMaybe(pattern)

Same astake(pattern) but does not automatically terminate the Saga on anEND action. Instead all Sagas blocked on a take Effect will get theEND object.

Notes

takeMaybe got its name from the FP analogy - it's like instead of having a return type ofACTION (with automatic handling) we can have a type ofMaybe(ACTION) so we can handle both cases:

  • case when there is aJust(ACTION) (we have an action)
  • the case ofNOTHING (channel was closed*). i.e. we need some way to map overEND
  • internally alldispatched actions are going through thestdChannel which is getting closed whendispatch(END) happens

take(channel)

Creates an Effect description that instructs the middleware to wait for a specified message from the provided Channel. If the channel is already closed, then the Generator will immediately terminate following the same process described above fortake(pattern).

takeMaybe(channel)

Same astake(channel) but does not automatically terminate the Saga on anEND action. Instead all Sagas blocked on a take Effect will get theEND object. See morehere

takeEvery(pattern, saga, ...args)

Spawns asaga on each action dispatched to the Store that matchespattern.

  • pattern: String | Array | Function - for more information see docs fortake(pattern)

  • saga: Function - a Generator function

  • args: Array<any> - arguments to be passed to the started task.takeEvery will add the incoming action to the argument list (i.e. the action will be the last argument provided tosaga)

Example

In the following example, we create a basic taskfetchUser. We usetakeEvery to start a newfetchUser task on each dispatchedUSER_REQUESTED action:

import{ takeEvery}from`redux-saga/effects`

function*fetchUser(action){
...
}

function*watchFetchUser(){
yieldtakeEvery('USER_REQUESTED', fetchUser)
}

Notes

takeEvery is a high-level API built usingtake andfork. Here is how the helper could be implemented using the low-level Effects

consttakeEvery=(patternOrChannel, saga,...args)=>fork(function*(){
while(true){
const action=yieldtake(patternOrChannel)
yieldfork(saga,...args.concat(action))
}
})

takeEvery allows concurrent actions to be handled. In the example above, when aUSER_REQUESTEDaction is dispatched, a newfetchUser task is started even if a previousfetchUser is still pending(for example, the user clicks on aLoad User button 2 consecutive times at a rapid rate, the 2ndclick will dispatch aUSER_REQUESTED action while thefetchUser fired on the first one hasn't yet terminated)

takeEvery doesn't handle out of order responses from tasks. There is no guarantee that the tasks willterminate in the same order they were started. To handle out of order responses, you may considertakeLatestbelow.

takeEvery(channel, saga, ...args)

You can also pass in a channel as argument and the behaviour is the same astakeEvery(pattern, saga, ...args).

takeLatest(pattern, saga, ...args)

Forks asaga on each action dispatched to the Store that matchespattern. And automatically cancelsany previoussaga task started previously if it's still running.

Each time an action is dispatched to the store. And if this action matchespattern,takeLateststarts a newsaga task in the background. If asaga task was started previously (on the last action dispatchedbefore the actual action), and if this task is still running, the task will be cancelled.

  • pattern: String | Array | Function - for more information see docs fortake(pattern)

  • saga: Function - a Generator function

  • args: Array<any> - arguments to be passed to the started task.takeLatest will add theincoming action to the argument list (i.e. the action will be the last argument provided tosaga)

Example

In the following example, we create a basic taskfetchUser. We usetakeLatest tostart a newfetchUser task on each dispatchedUSER_REQUESTED action. SincetakeLatestcancels any pending task started previously, we ensure that if a user triggers multiple consecutiveUSER_REQUESTED actions rapidly, we'll only conclude with the latest action

import{ takeLatest}from`redux-saga/effects`

function*fetchUser(action){
...
}

function*watchLastFetchUser(){
yieldtakeLatest('USER_REQUESTED', fetchUser)
}

Notes

takeLatest is a high-level API built usingtake andfork. Here is how the helper could be implemented using the low-level Effects

consttakeLatest=(patternOrChannel, saga,...args)=>fork(function*(){
let lastTask
while(true){
const action=yieldtake(patternOrChannel)
if(lastTask){
yieldcancel(lastTask)// cancel is no-op if the task has already terminated
}
lastTask=yieldfork(saga,...args.concat(action))
}
})

takeLatest(channel, saga, ...args)

You can also pass in a channel as argument and the behaviour is the same astakeLatest(pattern, saga, ...args).

takeLeading(pattern, saga, ...args)

Spawns asaga on each action dispatched to the Store that matchespattern.After spawning a task once, it blocks until spawned saga completes and then starts to listen for apattern again.

In short,takeLeading is listening for the actions when it doesn't run a saga.

  • pattern: String | Array | Function - for more information see docs fortake(pattern)

  • saga: Function - a Generator function

  • args: Array<any> - arguments to be passed to the started task.takeLeading will add theincoming action to the argument list (i.e. the action will be the last argument provided tosaga)

Example

In the following example, we create a basic taskfetchUser. We usetakeLeading tostart a newfetchUser task on each dispatchedUSER_REQUESTED action. SincetakeLeadingignores any new coming task after it's started, we ensure that if a user triggers multiple consecutiveUSER_REQUESTED actions rapidly, we'll only keep on running with the leading action

import{ takeLeading}from`redux-saga/effects`

function*fetchUser(action){
...
}

function*watchLastFetchUser(){
yieldtakeLeading('USER_REQUESTED', fetchUser)
}

Notes

takeLeading is a high-level API built usingtake andcall. Here is how the helper could be implemented using the low-level Effects

consttakeLeading=(patternOrChannel, saga,...args)=>fork(function*(){
while(true){
const action=yieldtake(patternOrChannel);
yieldcall(saga,...args.concat(action));
}
})

takeLeading(channel, saga, ...args)

You can also pass in a channel as argument and the behavior is the same astakeLeading(pattern, saga, ...args).

put(action)

Creates an Effect description that instructs the middleware to schedule the dispatching of an action to the store. This dispatch may not be immediate since other tasks might lie ahead in the saga task queue or still be in progress.

You can, however, expect the store to be updated in the current stack frame (i.e. by the next line of code afteryield put(action)) unless you have other Redux middlewares with asynchronous flows that delay the propagation of the action.

Downstream errors (e.g. from the reducer) will be bubbled up.

putResolve(action)

Just likeput but the effect is blocking (if promise is returned fromdispatch it will wait for its resolution) and will bubble up errors from downstream.

put(channel, action)

Creates an Effect description that instructs the middleware to put an action into the provided channel.

This effect is blocking if the put isnot buffered but immediately consumed by takers. If an erroris thrown in any of these takers it will bubble back into the saga.

call(fn, ...args)

Creates an Effect description that instructs the middleware to call the functionfn withargs as arguments.

  • fn: Function - A Generator function, or normal function which either returns a Promise as result, or any other value.
  • args: Array<any> - An array of values to be passed as arguments tofn

Notes

fn can be either anormal or a Generator function.

The middleware invokes the function and examines its result.

If the result is an Iterator object, the middleware will run that Generator function, just like it did with thestartup Generators (passed to the middleware on startup). The parent Generator will besuspended until the child Generator terminates normally, in which case the parent Generatoris resumed with the value returned by the child Generator. Or until the child aborts with someerror, in which case an error will be thrown inside the parent Generator.

Iffn is a normal function and returns a Promise, the middleware will suspend the Generator until the Promise issettled. After the promise is resolved the Generator is resumed with the resolved value, or if the Promiseis rejected an error is thrown inside the Generator.

If the result is not an Iterator object nor a Promise, the middleware will immediately return that value back to the saga,so that it can resume its execution synchronously.

When an error is thrown inside the Generator, if it has atry/catch block surrounding thecurrentyield instruction, the control will be passed to thecatch block. Otherwise,the Generator aborts with the raised error, and if this Generator was called by anotherGenerator, the error will propagate to the calling Generator.

call([context, fn], ...args)

Same ascall(fn, ...args) but supports passing athis context tofn. This is useful toinvoke object methods.

call([context, fnName], ...args)

Same ascall([context, fn], ...args) but supports passing afn as string. Useful for invoking object's methods, i.e.yield call([localStorage, 'getItem'], 'redux-saga')

call({context, fn}, ...args)

Same ascall([context, fn], ...args) but supports passingcontext andfn as properties of an object, i.e.yield call({context: localStorage, fn: localStorage.getItem}, 'redux-saga').fn can be a string or a function.

apply(context, fn, [args])

Alias forcall([context, fn], ...args).

cps(fn, ...args)

Creates an Effect description that instructs the middleware to invokefn as a Node style function.

  • fn: Function - a Node style function. i.e. a function which accepts in addition to its arguments,an additional callback to be invoked byfn when it terminates. The callback accepts two parameters,where the first parameter is used to report errors while the second is used to report successful results

  • args: Array<any> - an array to be passed as arguments forfn

Notes

The middleware will perform a callfn(...arg, cb). Thecb is a callback passed by the middleware tofn. Iffn terminates normally, it must callcb(null, result) to notify the middlewareof a successful result. Iffn encounters some error, then it must callcb(error) in order tonotify the middleware that an error has occurred.

The middleware remains suspended untilfn terminates.

cps([context, fn], ...args)

Supports passing athis context tofn (object method invocation)

cps({context, fn}, ...args)

Same ascps([context, fn], ...args) but supports passingcontext andfn as properties of an object.fn can be a string or a function.

fork(fn, ...args)

Creates an Effect description that instructs the middleware to perform anon-blocking call onfn

Arguments

  • fn: Function - A Generator function, or normal function which returns a Promise as result
  • args: Array<any> - An array of values to be passed as arguments tofn

returns aTask object.

Notes

fork, likecall, can be used to invoke both normal and Generator functions. But, the calls arenon-blocking, the middleware doesn't suspend the Generator while waiting for the result offn.Instead as soon asfn is invoked, the Generator resumes immediately.

fork, alongsiderace, is a central Effect for managing concurrency between Sagas.

The result ofyield fork(fn ...args) is aTask object. An object with some usefulmethods and properties.

All forked tasks areattached to their parents. When the parent terminates the execution of itsown body of instructions, it will wait for all forked tasks to terminate before returning.

Error propagation

Errors from child tasks automatically bubble up to their parents. If any forked task raises an uncaught error, thenthe parent task will abort with the child Error, and the whole Parent's execution tree (i.e. forked tasks + themain task represented by the parent's body if it's still running) will be cancelled.

Cancellation of a forked Task will automatically cancel all forked tasks that are still executing. It'llalso cancel the current Effect where the cancelled task was blocked (if any).

If a forked task failssynchronously (ie: fails immediately after its execution before performing anyasync operation), then no Task is returned, instead the parent will be aborted as soon as possible (since bothparent and child execute in parallel, the parent will abort as soon as it takes notice of the child failure).

To createdetached forks, usespawn instead.

fork([context, fn], ...args)

Supports invoking forked functions with athis context

fork({context, fn}, ...args)

Same asfork([context, fn], ...args) but supports passingcontext andfn as properties of an object.fn can be a string or a function.

spawn(fn, ...args)

Same asfork(fn, ...args) but creates adetached task. A detached task remains independent from its parent and acts likea top-level task. The parent will not wait for detached tasks to terminate before returning and all events which may affect theparent or the detached task are completely independents (error, cancellation).

spawn([context, fn], ...args)

Supports spawning functions with athis context

join(task)

Creates an Effect description that instructs the middleware to wait for the resultof a previously forked task.

  • task: Task - ATask object returned by a previousfork

Notes

join will resolve to the same outcome of the joined task (success or error). If the joinedtask is cancelled, the cancellation will also propagate to the Saga executing the joineffect. Similarly, any potential callers of those joiners will be cancelled as well.

join([...tasks])

Creates an Effect description that instructs the middleware to wait for the results of previously forked tasks.

  • tasks: Array<Task> - ATask is the object returned by a previousfork

Notes

It wraps the array of tasks injoin effects, roughly becoming the equivalent ofyield tasks.map(t => join(t)).

cancel(task)

Creates an Effect description that instructs the middleware to cancel a previously forked task.

  • task: Task - ATask object returned by a previousfork

Notes

To cancel a running task, the middleware will invokereturn on the underlying Generatorobject. This will cancel the current Effect in the task and jump to the finally block (if defined).

Inside the finally block, you can execute any cleanup logic or dispatch some action to keep thestore in a consistent state (e.g. reset the state of a spinner to false when an ajax requestis cancelled). You can check inside the finally block if a Saga was cancelled by issuingayield cancelled().

Cancellation propagates downward to child sagas. When cancelling a task, the middleware will alsocancel the current Effect (where the task is currently blocked). If the current Effectis a call to another Saga, it will be also cancelled. When cancelling a Saga, allattachedforks (sagas forked usingyield fork()) will be cancelled. This means that cancellationeffectively affects the whole execution tree that belongs to the cancelled task.

cancel is a non-blocking Effect. i.e. the Saga executing it will resume immediately afterperforming the cancellation.

For functions which return Promise results, you can plug your own cancellation logicby attaching a[CANCEL] to the promise.

The following example shows how to attach cancellation logic to a Promise result:

import{CANCEL}from'redux-saga'
import{ fork, cancel}from'redux-saga/effects'

functionmyApi(){
const promise=myXhr(...)

promise[CANCEL]=()=> myXhr.abort()
return promise
}

function*mySaga(){

const task=yieldfork(myApi)

// ... later
// will call promise[CANCEL] on the result of myApi
yieldcancel(task)
}

redux-saga will automatically cancel jqXHR objects using theirabort method.

cancel([...tasks])

Creates an Effect description that instructs the middleware to cancel previously forked tasks.

  • tasks: Array<Task> - ATask is the object returned by a previousfork

Notes

It wraps the array of tasks incancel effects, roughly becoming the equivalent ofyield tasks.map(t => cancel(t)).

cancel()

Creates an Effect description that instructs the middleware to cancel a task in which it has been yielded (self-cancellation).It allows to reuse destructor-like logic inside afinally blocks for both outer (cancel(task)) and self (cancel()) cancellations.

Example

function*deleteRecord({ payload}){
try{
const{ confirm, deny}=yieldcall(prompt);
if(confirm){
yieldput(actions.deleteRecord.confirmed())
}
if(deny){
yieldcancel()
}
}catch(e){
// handle failure
}finally{
if(yieldcancelled()){
// shared cancellation logic
yieldput(actions.deleteRecord.cancel(payload))
}
}
}

select(selector, ...args)

Creates an effect that instructs the middleware to invoke the provided selector on thecurrent Store's state (i.e. returns the result ofselector(getState(), ...args)).

  • selector: Function - a function(state, ...args) => args. It takes thecurrent state and optionally some arguments and returns a slice of the current Store's state

  • args: Array<any> - optional arguments to be passed to the selector in addition ofgetState.

Ifselect is called without argument (i.e.yield select()) then the effect is resolvedwith the entire state (the same result of agetState() call).

It's important to note that when an action is dispatched to the store, the middleware firstforwards the action to the reducers and then notifies the Sagas. This means that when you query theStore's State, you get the Stateafter the action has been applied.However, this behavior is only guaranteed if all subsequent middlewares callnext(action) synchronously. If any subsequent middleware callsnext(action) asynchronously (which is unusual but possible), then the sagas will get the state frombefore the action is applied. Therefore it is recommended to review the source of each subsequent middleware to ensure it callsnext(action) synchronously, or else ensure that redux-saga is the last middleware in the call chain.

Notes

Preferably, a Saga should be autonomous and should not depend on the Store's state. This makesit easy to modify the state implementation without affecting the Saga code. A saga should preferablydepend only on its own internal control state when possible. But sometimes, one couldfind it more convenient for a Saga to query the state instead of maintaining the needed data by itself(for example, when a Saga duplicates the logic of invoking some reducer to compute a state that wasalready computed by the Store).

For example, suppose we have this state shape in our application:

state={
cart:{...}
}

We can create aselector, i.e. a function which knows how to extract thecart data from the State:

./selectors

exportconstgetCart=state=> state.cart

Then we can use that selector from inside a Saga using theselect Effect:

./sagas.js

import{ take, fork, select}from'redux-saga/effects'
import{ getCart}from'./selectors'

function*checkout(){
// query the state using the exported selector
const cart=yieldselect(getCart)

// ... call some API endpoint then dispatch a success/error action
}

exportdefaultfunction*rootSaga(){
while(true){
yieldtake('CHECKOUT_REQUEST')
yieldfork(checkout)
}
}

checkout can get the needed information directly by usingselect(getCart). The Saga is coupled only with thegetCart selector. If we have many Sagas (or React Components) that needs to access thecart slice, they will all be coupled to the same functiongetCart. And if we now change the state shape, we need only to updategetCart.

actionChannel(pattern, [buffer])

Creates an effect that instructs the middleware to queue the actions matchingpattern using an event channel. Optionally, you can provide a buffer to control buffering of the queued actions.

  • pattern: - see API fortake(pattern)
  • buffer: Buffer - aBuffer object

Example

The following code creates a channel to buffer allUSER_REQUEST actions. Note that even the Saga may be blockedon thecall effect. All actions that come while it's blocked are automatically buffered. This causes the Sagato execute the API calls one at a time

import{ actionChannel, call}from'redux-saga/effects'
importapifrom'...'

function*takeOneAtMost(){
const chan=yieldactionChannel('USER_REQUEST')
while(true){
const{payload}=yieldtake(chan)
yieldcall(api.getUser, payload)
}
}

flush(channel)

Creates an effect that instructs the middleware to flush all buffered items from the channel. Flushed items are returned back to the saga, so they can be utilized if needed.

  • channel: Channel - aChannel Object.

Example


function*saga(){
const chan=yieldactionChannel('ACTION')

try{
while(true){
const action=yieldtake(chan)
// ...
}
}finally{
const actions=yieldflush(chan)
// ...
}

}

cancelled()

Creates an effect that instructs the middleware to return whether this generator has been cancelled. Typicallyyou use this Effect in a finally block to run Cancellation specific code

Example


function*saga(){
try{
// ...
}finally{
if(yieldcancelled()){
// logic that should execute only on Cancellation
}
// logic that should execute in all situations (e.g. closing a channel)
}
}

setContext(props)

Creates an effect that instructs the middleware to update its own context. This effect extendssaga's context instead of replacing it.

getContext(prop)

Creates an effect that instructs the middleware to return a specific property of saga's context.

delay(ms, [val])

Returns an effect descriptor to block execution forms milliseconds and returnval value.

throttle(ms, pattern, saga, ...args)

Spawns asaga on an action dispatched to the Store that matchespattern. After spawning a task it's still accepting incoming actions into the underlyingbuffer, keeping at most 1 (the most recent one), but in the same time holding up with spawning new task forms milliseconds (hence its name -throttle). Purpose of this is to ignore incoming actions for a given period of time while processing a task.

  • ms: Number - length of a time window in milliseconds during which actions will be ignored after the action starts processing

  • pattern: String | Array | Function - for more information see docs fortake(pattern)

  • saga: Function - a Generator function

  • args: Array<any> - arguments to be passed to the started task.throttle will add theincoming action to the argument list (i.e. the action will be the last argument provided tosaga)

Example

In the following example, we create a basic taskfetchAutocomplete. We usethrottle tostart a newfetchAutocomplete task on dispatchedFETCH_AUTOCOMPLETE action. However sincethrottle ignores consecutiveFETCH_AUTOCOMPLETE for some time, we ensure that user won't flood our server with requests.

import{ call, put, throttle}from`redux-saga/effects`

function*fetchAutocomplete(action){
const autocompleteProposals=yieldcall(Api.fetchAutocomplete, action.text)
yieldput({type:'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
}

function*throttleAutocomplete(){
yieldthrottle(1000,'FETCH_AUTOCOMPLETE', fetchAutocomplete)
}

Notes

throttle is a high-level API built usingtake,fork andactionChannel. Here is how the helper could be implemented using the low-level Effects

constthrottle=(ms, pattern, task,...args)=>fork(function*(){
const throttleChannel=yieldactionChannel(pattern, buffers.sliding(1))

while(true){
const action=yieldtake(throttleChannel)
yieldfork(task,...args, action)
yielddelay(ms)
}
})

throttle(ms, channel, saga, ...args)

You can also handle a channel as argument and the behaviour is the same asthrottle(ms, pattern, saga, ..args)

debounce(ms, pattern, saga, ...args)

Spawns asaga on an action dispatched to the Store that matchespattern. Saga will be called after it stops takingpattern actions forms milliseconds. Purpose of this is to prevent calling saga until the actions are settled off.

  • ms: Number - defines how many milliseconds should elapse since the last timepattern action was fired to call thesaga

  • pattern: String | Array | Function - for more information see docs fortake(pattern)

  • saga: Function - a Generator function

  • args: Array<any> - arguments to be passed to the started task.debounce will add theincoming action to the argument list (i.e. the action will be the last argument provided tosaga)

Example

In the following example, we create a basic taskfetchAutocomplete. We usedebounce todelay callingfetchAutocomplete saga until we stop receive anyFETCH_AUTOCOMPLETE events for at least1000 ms.

import{ call, put, debounce}from`redux-saga/effects`

function*fetchAutocomplete(action){
const autocompleteProposals=yieldcall(Api.fetchAutocomplete, action.text)
yieldput({type:'FETCHED_AUTOCOMPLETE_PROPOSALS', proposals: autocompleteProposals})
}

function*debounceAutocomplete(){
yielddebounce(1000,'FETCH_AUTOCOMPLETE', fetchAutocomplete)
}

Notes

debounce is a high-level API built usingtake,delay,race andfork. Here is how the helper could be implemented using the low-level Effects

constdebounce=(ms, pattern, task,...args)=>fork(function*(){
while(true){
let action=yieldtake(pattern)

while(true){
const{ debounced, latestAction}=yieldrace({
debounced:delay(ms),
latestAction:take(pattern)
})

if(debounced){
yieldfork(task,...args, action)
break
}

action= latestAction
}
}
})

debounce(ms, channel, saga, ...args)

You can also handle a channel as argument and the behaviour is the same asdebounce(ms, pattern, saga, ..args)

retry(maxTries, delay, fn, ...args)

Creates an Effect description that instructs the middleware to call the functionfn withargs as arguments.In case of failure will try to make another call afterdelay milliseconds, if a number of attempts <maxTries.

  • maxTries: Number - maximum calls count.
  • delay: Number - length of a time window in milliseconds betweenfn calls.
  • fn: Function - A Generator function, or normal function which either returns a Promise as a result, or any other value.
  • args: Array<any> - An array of values to be passed as arguments tofn

Example

In the following example, we create a basic taskretrySaga. We useretry to try to fetch our API 3 times with 10 second interval. Ifrequest fails first time thanretry will callrequest one more time while calls count less than 3.

import{ put, retry}from'redux-saga/effects'
import{ request}from'some-api';

function*retrySaga(data){
try{
constSECOND=1000
const response=yieldretry(3,10*SECOND, request, data)
yieldput({ type:'REQUEST_SUCCESS', payload: response})
}catch(error){
yieldput({ type:'REQUEST_FAIL', payload:{ error}})
}
}

Notes

retry is a high-level API built usingdelay andcall.Here is how the helper could be implemented using the low-level Effects

Effect combinators

race(effects)

Creates an Effect description that instructs the middleware to run aRace betweenmultiple Effects (this is similar to howPromise.race([...]) behaves).

effects: Object - a dictionary Object of the form {label: effect, ...}

Example

The following example runs a race between two effects:

  1. A call to a functionfetchUsers which returns a Promise
  2. ACANCEL_FETCH action which may be eventually dispatched on the Store
import{ take, call, race}from`redux-saga/effects`
importfetchUsersfrom'./path/to/fetchUsers'

function*fetchUsersSaga(){
const{ response, cancel}=yieldrace({
response:call(fetchUsers),
cancel:take(CANCEL_FETCH)
})
}

Ifcall(fetchUsers) resolves first, the result ofrace will be an objectwith a single keyed object{response: result} whereresult is the resolved result offetchUsers.

Ifcall(fetchUsers) rejects first,race throws the rejection reason.

If an action of typeCANCEL_FETCH is dispatched on the Store beforefetchUsers completes, the resultwill be a single keyed object{cancel: action}, where action is the dispatched action.

Notes

When resolving arace, the middleware automatically cancels all the losing Effects.

race([...effects]) (with Array)

The same asrace(effects) but lets you pass in an array of effects.

Example

The following example runs a race between two effects:

  1. A call to a functionfetchUsers which returns a Promise
  2. ACANCEL_FETCH action which may be eventually dispatched on the Store
import{ take, call, race}from`redux-saga/effects`
importfetchUsersfrom'./path/to/fetchUsers'

function*fetchUsersSaga(){
const[response, cancel]=yieldrace([
call(fetchUsers),
take(CANCEL_FETCH)
])
}

Ifcall(fetchUsers) resolves first,response will be an result offetchUsers andcancel will beundefined.

Ifcall(fetchUsers) rejects first,race throws the rejection reason.

If an action of typeCANCEL_FETCH is dispatched on the Store beforefetchUsers completes,response will beundefined andcancel will be the dispatched action.

all([...effects]) - parallel effects

Creates an Effect description that instructs the middleware to run multiple Effectsin parallel and wait for all of them to complete. It's quite the corresponding API to standardPromise#all.

Example

The following example runs two blocking calls in parallel:

import{ fetchCustomers, fetchProducts}from'./path/to/api'
import{ all, call}from`redux-saga/effects`

function*mySaga(){
const[customers, products]=yieldall([
call(fetchCustomers),
call(fetchProducts)
])
}

all(effects)

The same asall([...effects]) but lets you to pass in a dictionary object of effects with labels, just likerace(effects)

  • effects: Object - a dictionary Object of the form {label: effect, ...}

Example

The following example runs two blocking calls in parallel:

import{ fetchCustomers, fetchProducts}from'./path/to/api'
import{ all, call}from`redux-saga/effects`

function*mySaga(){
const{ customers, products}=yieldall({
customers:call(fetchCustomers),
products:call(fetchProducts)
})
}

Notes

When running Effects in parallel, the middleware suspends the Generator until one of the following occurs:

  • All the Effects completed with success: resumes the Generator with an array containing the results of all Effects.

  • One of the Effects was rejected before all the effects complete: throws the rejection error inside the Generator.

Interfaces

Task

The Task interface specifies the result of running a Saga usingfork,middleware.run orrunSaga.

methodreturn value
task.isRunning()true if the task hasn't yet returned or thrown an error
task.isCancelled()true if the task has been cancelled
task.result()task return value. `undefined` if task is still running
task.error()task thrown error. `undefined` if task is still running
task.toPromise()a Promise which is either:
  • resolved with task's return value
  • rejected with task's thrown error
task.cancel()Cancels the task (If it is still running)

Channel

A channel is an object used to send and receive messages between tasks. Messages from senders are queued until an interested receiver request a message, and registered receiver is queued until a message is available.

Every channel has an underlying buffer which defines the buffering strategy (fixed size, dropping, sliding)

The Channel interface defines 3 methods:take,put andclose

Channel.take(callback): used to register a taker. The take is resolved using the following rules

  • If the channel has buffered messages, thencallback will be invoked with the next message from the underlying buffer (usingbuffer.take())
  • If the channel is closed and there are no buffered messages, thencallback is invoked withEND
  • Otherwisecallback will be queued until a message is put into the channel

Channel.put(message): Used to put message on the buffer. The put will be handled using the following rules

  • If the channel is closed, then the put will have no effect.
  • If there are pending takers, then invoke the oldest taker with the message.
  • Otherwise put the message on the underlying buffer

Channel.flush(callback): Used to extract all buffered messages from the channel. The flush is resolved using the following rules

  • If the channel is closed and there are no buffered messages, thencallback is invoked withEND
  • Otherwisecallback is invoked with all buffered messages.

Channel.close(): closes the channel which means no more puts will be allowed. All pending takers will be invoked withEND.

Buffer

Used to implement the buffering strategy for a channel. The Buffer interface defines 3 methods:isEmpty,put andtake

  • isEmpty(): returns true if there are no messages on the buffer. A channel calls this method whenever a new taker is registered
  • put(message): used to put new message in the buffer. Note the Buffer can choose to not store the message(e.g. a dropping buffer can drop any new message exceeding a given limit)
  • take() used to retrieve any buffered message. Note the behavior of this method has to be consistent withisEmpty

SagaMonitor

Used by the middleware to dispatch monitoring events. Actually the middleware dispatches 6 events:

  • When a root saga is started (viarunSaga orsagaMiddleware.run) the middleware invokessagaMonitor.rootSagaStarted

  • When an effect is triggered (viayield someEffect) the middleware invokessagaMonitor.effectTriggered

  • If the effect is resolved with success the middleware invokessagaMonitor.effectResolved

  • If the effect is rejected with an error the middleware invokessagaMonitor.effectRejected

  • If the effect is cancelled the middleware invokessagaMonitor.effectCancelled

  • Finally, the middleware invokessagaMonitor.actionDispatched when a Redux action is dispatched.

Below the signature for each method

  • sagaMonitor.rootSagaStarted(options) : where options is an object with the following fields

    • effectId : Number - Unique ID assigned to this root saga execution

    • saga : Function - The generator function that starts to run

    • args : Array - The arguments passed to the generator function

  • effectTriggered(options)

    • effectId : Number - Unique ID assigned to the yielded effect

    • parentEffectId : Number - ID of the parent Effect. In the case of arace orparallel effect, alleffects yielded inside will have the direct race/parallel effect as a parent. In case of a top-level effect, theparent will be the containing Saga

    • label : String - In case of arace/all effect, all child effects will be assigned as label the correspondingkeys of the object passed torace/all

    • effect : Object - the yielded effect itself

  • effectResolved(effectId, result)

    • effectId : Number - The ID of the yielded effect

    • result : any - The result of the successful resolution of the effect. In case offork orspawn effects,the result will be aTask object.

  • effectRejected(effectId, error)

    • effectId : Number - The ID of the yielded effect

    • error : any - Error raised with the rejection of the effect

  • effectCancelled(effectId)

    • effectId : Number - The ID of the yielded effect
  • actionDispatched(action)

    • action : Object - The dispatched Redux action. If the action was dispatched by a Sagathen the action will have a propertySAGA_ACTION set to true (SAGA_ACTION can be imported from@redux-saga/symbols).

External API


runSaga(options, saga, ...args)

Allows starting sagas outside the Redux middleware environment. Useful if you want toconnect a Saga to external input/output, other than store actions.

runSaga returns a Task object. Just like the one returned from afork effect.

Notes

The{channel, dispatch} is used to fulfilltake andput Effects. This defines the Input/Outputinterface of the Saga.

channel is used to fulfilltake(PATTERN) effects. Every time something gets put on the channel it'snotifying all pending internal listeners. If the Saga is blocked on atake effect, andif the take pattern matches the currently incoming input, the Saga is resumed with that input.

dispatch is used to fulfillput effects. Each time the Saga emits ayield put(output),dispatchis invoked with output.

An example how to use this API may be foundhere.

Utils

channel([buffer])

A factory method that can be used to create Channels. You can optionally pass it a bufferto control how the channel buffers the messages.

By default, if no buffer is provided, the channel will queue incoming messages up to 10 until interested takers are registered. The default buffering will deliver message using a FIFO strategy: a new taker will be delivered the oldest message in the buffer.

eventChannel(subscribe, [buffer])

Creates channel that will subscribe to an event source using thesubscribe method. Incoming events from the event source will be queued in the channel until interested takers are registered.

  • subscribe: Function used to subscribe to the underlying event source. The function must return an unsubscribe function to terminate the subscription.

  • buffer: Buffer optional Buffer object to buffer messages on this channel. If not provided, messages will not be bufferedon this channel.

To notify the channel that the event source has terminated, you can notify the provided subscriber with anEND

Example

In the following example we create an event channel that will subscribe to asetInterval

constcountdown=(secs)=>{
returneventChannel(emitter=>{
const iv=setInterval(()=>{
console.log('countdown', secs)
secs-=1
if(secs>0){
emitter(secs)
}else{
emitter(END)
clearInterval(iv)
console.log('countdown terminated')
}
},1000);
return()=>{
clearInterval(iv)
console.log('countdown cancelled')
}
}
)
}

buffers

Provides some common buffers

  • buffers.none(): no buffering, new messages will be lost if there are no pending takers

  • buffers.fixed(limit): new messages will be buffered up tolimit. Overflow will raise an Error. Omitting alimit value will result in a limit of 10.

  • buffers.expanding(initialSize): likefixed but Overflow will cause the buffer to expand dynamically.

  • buffers.dropping(limit): same asfixed but Overflow will silently drop the messages.

  • buffers.sliding(limit): same asfixed but Overflow will insert the new message at the end and drop the oldest message in the buffer.

cloneableGenerator(generatorFunc)

Takes a generator function (function*) and returns a generator function.All generators instanciated from this function will be cloneable.For testing purpose only.

Example

This is useful when you want to test a different branch of a saga without having to replay the actions that lead to it.

import{ cloneableGenerator}from'@redux-saga/testing-utils';

function*oddOrEven(){
// some stuff are done here
yield1;
yield2;
yield3;

const userInput=yield'enter a number';
if(userInput%2===0){
yield'even';
}else{
yield'odd'
}
}

test('my oddOrEven saga',assert=>{
const data={};
data.gen=cloneableGenerator(oddOrEven)();

assert.equal(
data.gen.next().value,
1,
'it should yield 1'
);

assert.equal(
data.gen.next().value,
2,
'it should yield 2'
);

assert.equal(
data.gen.next().value,
3,
'it should yield 3'
);

assert.equal(
data.gen.next().value,
'enter a number',
'it should ask for a number'
);

assert.test('even number is given',a=>{
// we make a clone of the generator before giving the number;
data.clone= data.gen.clone();

a.equal(
data.gen.next(2).value,
'even',
'it should yield "even"'
);

a.equal(
data.gen.next().done,
true,
'it should be done'
);

a.end();
});

assert.test('odd number is given',a=>{

a.equal(
data.clone.next(1).value,
'odd',
'it should yield "odd"'
);

a.equal(
data.clone.next().done,
true,
'it should be done'
);

a.end();
});

assert.end();
});

createMockTask()

Returns an object that mocks a task.For testing purposes only.See Task Cancellation docs for more information.)

Cheatsheets

Blocking / Non-blocking

NameBlocking
takeEveryNo
takeLatestNo
takeLeadingNo
throttleNo
debounceNo
retryYes
takeYes
take(channel)Sometimes (see API reference)
takeMaybeYes
putNo
putResolveYes
put(channel, action)No
callYes
applyYes
cpsYes
forkNo
spawnNo
joinYes
cancelNo
selectNo
actionChannelNo
flushYes
cancelledYes
raceYes
delayYes
allBlocks if there is a blocking effect in the array or object

[8]ページ先頭

©2009-2025 Movatter.jp