Original Reference
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 matches pattern is dispatched.
The result of yield take(pattern) is an action object being dispatched.
pattern is interpreted using the following rules:
If take 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 if pattern(action) is true (e.g. take(action => action.entities) will match all actions having a (truthy) entities field.)
Note: if the pattern function has toString defined on it, action.type will be tested against pattern.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 if action.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 that action.type is matched against all items in the array (e.g. take([INCREMENT, DECREMENT]) and that would match either actions of type INCREMENT or DECREMENT).
The middleware provides a special action END. 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.
Original Reference
Creates an Effect description that instructs the middleware to cancel a previously forked task.
task: Task - A Task object returned by a previous fork
Notes
To cancel a running task, the middleware will invoke return on the underlying Generator
object. 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 the
store in a consistent state (e.g. reset the state of a spinner to false when an ajax request
is cancelled). You can check inside the finally block if a Saga was cancelled by issuing
a yield cancelled().
Cancellation propagates downward to child sagas. When cancelling a task, the middleware will also
cancel the current Effect (where the task is currently blocked). If the current Effect
is a call to another Saga, it will be also cancelled. When cancelling a Saga, all attached
forks (sagas forked using yield fork()) will be cancelled. This means that cancellation
effectively 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 after
performing the cancellation.
For functions which return Promise results, you can plug your own cancellation logic
by 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 their abort method.
import{
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect,
import Fiber
Fiber}from'effect'
declareconst
const myApi: Effect.Effect<void,never,never>
myApi:
import Effect
@since ― 2.0.0
@since ― 2.0.0
@since ― 2.0.0
Effect.
interfaceEffect<out A,out E =never,out R =never>
The Effect interface defines a value that describes a workflow or job,
which can succeed or fail.
Details
The Effect interface represents a computation that can model a workflow
involving various types of operations, such as synchronous, asynchronous,
concurrent, and parallel interactions. It operates within a context of type
R, and the result can either be a success with a value of type A or a
failure with an error of type E. The Effect is designed to handle complex
interactions with external resources, offering advanced features such as
fiber-based concurrency, scheduling, interruption handling, and scalability.
This makes it suitable for tasks that require fine-grained control over
concurrency and error management.
To execute an Effect value, you need a Runtime, which provides the
environment necessary to run and manage the computation.
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to Use
Effect.gen allows you to write code that looks and behaves like synchronous
code, but it can handle asynchronous tasks, errors, and complex control flow
(like loops and conditions). It helps make asynchronous code more readable
and easier to manage.
The generator functions work similarly to async/await but with more
explicit control over the execution of effects. You can yield* values from
effects and return the final result at the end.
@example
import{ Effect }from"effect"
constaddServiceCharge=(amount:number)=> amount +1
constapplyDiscount= (
total:number,
discountRate:number
):Effect.Effect<number, Error>=>
discountRate ===0
? Effect.fail(newError("Discount rate cannot be zero"))
Returns an effect that forks this effect into its own separate fiber,
returning the fiber immediately, without waiting for it to begin executing
the effect.
You can use the fork method whenever you want to execute an effect in a
new fiber, concurrently and without "blocking" the fiber executing other
effects. Using fibers can be tricky, so instead of using this method
directly, consider other higher-level methods, such as raceWith,
zipPar, and so forth.
The fiber returned by this method has methods to interrupt the fiber and to
wait for it to finish executing the effect. See Fiber for more
information.
Whenever you use this method to launch a new fiber, the new fiber is
attached to the parent fiber's scope. This means when the parent fiber
terminates, the child fiber will be terminated as well, ensuring that no
fibers leak. This behavior is called "auto supervision", and if this
behavior is not desired, you may use the forkDaemon or forkIn methods.
Interrupts the fiber from whichever fiber is calling this method. If the
fiber has already exited, the returned effect will resume immediately.
Otherwise, the effect will resume when the fiber exits.
Original Reference
Creates an effect that instructs the middleware to invoke the provided selector on the
current Store’s state (i.e. returns the result of selector(getState(), ...args)).
selector: Function - a function (state, ...args) => args. It takes the
current 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 of getState.
If select is called without argument (i.e. yield select()) then the effect is resolved
with the entire state (the same result of a getState() call).
It’s important to note that when an action is dispatched to the store, the middleware first
forwards the action to the reducers and then notifies the Sagas. This means that when you query the
Store’s State, you get the State after the action has been applied.
However, this behavior is only guaranteed if all subsequent middlewares call next(action) synchronously. If any subsequent middleware calls next(action) asynchronously (which is unusual but possible), then the sagas will get the state from before the action is applied. Therefore it is recommended to review the source of each subsequent middleware to ensure it calls next(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 makes
it easy to modify the state implementation without affecting the Saga code. A saga should preferably
depend only on its own internal control state when possible. But sometimes, one could
find 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 was
already computed by the Store).
For example, suppose we have this state shape in our application:
state ={
cart:{...}
}
We can create a selector, i.e. a function which knows how to extract the cart data from the State:
./selectors
exportconstgetCart=state=> state.cart
Then we can use that selector from inside a Saga using the select Effect:
// ... 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 using select(getCart). The Saga is coupled only with the getCart selector. If we have many Sagas (or React Components) that needs to access the cart slice, they will all be coupled to the same function getCart. And if we now change the state shape, we need only to update getCart.