Redux.Saga
- Package
- purescript-redux-saga
- Repository
- felixschl/purescript-redux-saga
#sagaMiddleware Source
sagaMiddleware :: forall eff state action. Saga' Unit state action action Unit -> Middleware eff state action _ _
#Saga' Source
newtype Saga' env state input output a
The Saga
monad is an opinionated, closed monad with a range of
functionality.
read-only environment, accessible via `MonadAsk` instance
/ your state container type (reducer)
| / the type this saga consumes (i.e. actions)
| | / the type of output this saga produces (i.e. actions)
| | | / the return value of this Saga
| | | | /
newtype Saga' env state input output a = ...
The env
parameter
The env
parameter gives us access to a read-only environment, accessible via
MonadAsk
instance. Forked computations have the opportunity to change this
environment for the execution of the fork without affecting the current thread's
env
.
type MyConfig = { apiUrl :: String }
logApiUrl :: ∀ state input output. Saga' MyConfig state input output Unit
logApiUrl = void do
{ apiUrl } <- ask
liftIO $ Console.log apiUrl
The state
parameter
The state
parameter gives us read access to the current application state via
the select
combinator. This value may change over time.
type MyState = { currentUser :: String }
logUser :: ∀ env input output. Saga' env MyState input output Unit
logUser = void do
{ currentUser } <- select
liftIO $ Console.log currentUser
The input
parameter
The input
parameter denotes the type of input this saga consumes. This in
combination with the output
parameter exposes sagas for what they naturally
are: pipes consuming input
and producing output
. Typically this input type
would correspond to your application's actions type.
data MyAction
= LoginRequest Username Password
| LogoutRequest
loginFlow :: ∀ env state output. Saga' env state MyAction MyAction Unit
loginFlow = forever do
take case _ of
LoginRequest user pw -> do
...
_ -> Nothing -- ignore other actions
The output
parameter
The output
parameter denotes the type of output this saga produces. Typically
this input type would correspond to your application's actions type. The
output
sagas produce is fed back into redux cycle by dispatching it to the
store.
data MyAction
= LoginRequest Username Password
| LogoutSuccess Username
| LogoutFailure Error
| LogoutRequest
loginFlow :: ∀ env state. Saga' env state MyAction MyAction Unit
loginFlow = forever do
take case _ of
LoginRequest user pw -> do
liftAff (attempt {- some I/O -}) >>= case _ of
Left err -> put $ LoginFailure err
Right v -> put $ LoginSuccess user
_ -> Nothing -- ignore other actions
The a
parameter
The a
parameter allows every saga to return a value, making it composable.
Here's an example of a contrived combinator
type MyAppConf = { apiUrl :: String }
type Account = { id :: String, email :: String }
getAccount
:: ∀ state input output
. String
-> Saga' MyAppConf state input output Account
getAccount id = do
{ apiUrl } <- ask
liftAff (API.getAccounts apiUrl)
-- later ...
saga
:: ∀ state input output
. Saga' MyAppConf state input output Unit
saga = do
account <- getAccount "123-dfa-123"
liftEff $ Console.log $ show account.email
Instances
Newtype (Saga' env state input action a) _
Applicative (Saga' env state input action)
Functor (Saga' env state input action)
Apply (Saga' env state input action)
Bind (Saga' env state input action)
Monad (Saga' env state input action)
MonadRec (Saga' env state input action)
MonadThrow Error (Saga' env state input action)
MonadError Error (Saga' env state input action)
Alt (Saga' env state input action)
MonadAsk env (Saga' env state input action)
MonadReader env (Saga' env state input action)
MonadIO (Saga' env state input action)
MonadEff eff (Saga' env state input action)
MonadAff eff (Saga' env state input action)
#GlobalState Source
type GlobalState state action = { api :: MiddlewareAPI (infinity :: INFINITY) state action, idSupply :: IdSupply }
#take Source
take :: forall a output input state env. (input -> Maybe (Saga' env state input output a)) -> Saga' env state input output a
take
blocks to receive an input value for which the given function
returns Just
a saga to execute. This saga is then executed on the same
thread, blocking until it finished running.
Example
data Action = SayFoo | SayBar
sayOnlyBar
:: ∀ env state Action Action
. Saga' env state Action Action Unit
sayOnlyBar = do
take case _ of
SayFoo -> Nothing
SayBar -> Just do
liftEff $ Console.log "Bar!"
#takeLatest Source
takeLatest :: forall a output input state env. (input -> Maybe (Saga' env state input output a)) -> Saga' env state input output a
Similar to take
but runs indefinitely, replacing previously spawned tasks
with new ones.
#fork Source
fork :: forall eff a output input state env. Saga' env state input output a -> Saga' env state input output (SagaFiber input a)
fork
puts a saga in the background
Example
helloWorld
:: ∀ env state input output
. Saga' env state input output Unit
helloWorld = do
fork $ do
liftAff $ delay $ 10000.0 # Milliseconds
liftAff $ Console.log "World!"
liftEff $ Console.log "Hello"
-- >> Hello
-- >> World!
fork
returns a SagaTask a
, which can later be joined using joinTask
or
canceled using cancelTask
.
important: A saga thread won't finish running until all attached forks have finished running!
#forkNamed' Source
forkNamed' :: forall eff a output input state newEnv env. String -> newEnv -> Saga' newEnv state input output a -> Saga' env state input output (SagaFiber input a)
Same as fork'
, but allows setting a name tag
#put Source
put :: forall state action input env. action -> Saga' env state input action Unit
put
emits an output
which gets dispatched to your redux store and in
turn becomes available to sagas.
Example
data Action = SayFoo | SayBar
sayOnlyBar
:: ∀ env state
. Saga' env state Action Action Unit
sayOnlyBar = do
put SayBar -- ^ trigger a `SayBar` action right away.
take case _ of
SayFoo -> Nothing
SayBar -> Just do
liftEff $ Console.log "Bar!"
-- >> Bar!
#select Source
select :: forall a output input state env. Saga' env state input output state
select
gives access the the current application state and it's output
varies over time as the application state changes.
Example
printState
:: ∀ env state input output
=> Show state
. Saga' env state input output Unit
printState = do
state <- select
liftAff $ Console.log $ show state
#joinTask Source
joinTask :: forall eff a output input state env. SagaFiber _ a -> Saga' env state input output a
joinTask
blocks until a given SagaTask
returns.
NOTE: Canceling the joining of a task will also cancel the task itself.
Example
helloWorld
:: ∀ env state input output
. Saga' env state input output Unit
helloWorld = do
task <- fork $ do
liftAff $ delay $ 10000.0 # Milliseconds
liftAff $ Console.log "World!"
joinTask task
liftEff $ Console.log "Hello"
-- >> World!
-- >> Hello
#cancelTask Source
cancelTask :: forall eff a output input state env. SagaFiber _ a -> Saga' env state input output Unit
cancelTask
cancels a given SagaTask
.
Example
helloWorld
:: ∀ env state input output
. Saga' env state input output Unit
helloWorld = do
task <- fork $ do
liftAff $ delay $ 10000.0 # Milliseconds
liftAff $ Console.log "World!"
cancelTask task
liftEff $ Console.log "Hello"
-- >> Hello
#channel Source
channel :: forall eff a state action input' input env. NameTag -> (Emitter input' -> IO Unit) -> Saga' env state (Either input input') action a -> Saga' env state input action (SagaFiber input a)
Forks a new saga, providing the caller with an Emitter
, allowing them to
inject arbitrary values into the forked saga. To discern between upstream
values and injected values, values are tagged Left
and Right
respectively.
#localEnv Source
localEnv :: forall a output input state env2 env. (env -> env2) -> Saga' env2 state input output a -> Saga' env state input output a
localEnv
runs a saga in a modified environment. This allows us to combine
sagas in multiple environments. For example, we could write sagas that
require access to certain values like account information without worrying
about "how" to manually pass those values along.
Example
printEnv
:: ∀ env state input output
=> Show env
. Saga' env state input output Unit
printEnv = do
env <- ask
liftEff $ Console.log $ show env
saga
:: ∀ env state input output
. Saga' env state input output Unit
saga = do
localEnv (const "Hello") printEnv
localEnv (const "World!") printEnv
-- >> Hello
-- >> World!
- Modules
- Redux.
Saga - Redux.
Saga. Combinators
Races two sagas in parallel The losing saga will be canceled via
cancelTask