Skip to main content

Effect

Effect is a container for async function.

It can be safely used in place of the original async function.

Arguments

  1. params (Params): parameters passed to effect

Returns

(Promise)

Example#

import {createEffect, createStore} from 'effector'
const fetchUserFx = createEffect(async ({id}) => {
const res = await fetch(`https://example.com/users/${id}`)
return res.json()
})
const users = createStore([]) // Default state
// add reducer for fetchUserFx.doneData event (triggered when handler resolved)
.on(fetchUserFx.doneData, (users, user) => [...users, user])
// subscribe to handler resolve
fetchUserFx.done.watch(({result, params}) => {
console.log(params) // => {id: 1}
console.log(result) // => resolved value
})
// subscribe to handler reject or throw error
fetchUserFx.fail.watch(({error, params}) => {
console.error(params) // => {id: 1}
console.error(error) // => rejected value
})
// you can replace function anytime
fetchUserFx.use(function anotherHandler() {})
// call effect with your params
fetchUserFx({id: 1})
// handle promise
const data = await fetchUserFx({id: 2})

Try it

Effect Methods#

use(handler)#

Provides a function, which will be called when effect is triggered.

Formulae#

effect.use(fn)
  • Set handler fn for effect
  • If effect already had an implementation at the time of the call, it will be replaced by a new one
  • Hint: current handler can be extracted with effect.use.getCurrent()
note

You must provide a handler either through .use method or handler property in createEffect, otherwise effect will throw with "no handler used in %effect name%" error when effect will be called

Arguments

  1. handler (Function): Function, that receives the first argument passed to an effect call.

Returns

(Effect): The same effect

Example#

const fetchUserReposFx = createEffect()
fetchUserReposFx.use(async params => {
console.log('fetchUserReposFx called with', params)
const url = `https://api.github.com/users/${params.name}/repos`
const req = await fetch(url)
return req.json()
})
fetchUserReposFx({name: 'zerobias'})
// => fetchUserRepos called with {name: 'zerobias'}

Try it


watch(watcher)#

Subscribe to effect calls.

Formulae#

const unwatch = effect.watch(fn)
  • Call fn on each effect call, pass payload of effect as argument to fn
  • When unwatch is called, stop calling fn

Arguments

  1. watcher (Watcher): A function that receives payload.

Returns

Subscription: Unsubscribe function.

Example#

import {createEffect} from 'effector'
const fx = createEffect(params => params)
fx.watch(params => {
console.log('effect called with value', params)
})
await fx(10)
// => effect called with value 10

Try it


prepend(fn)#

Creates an event, upon trigger it sends transformed data into the source event. Works kind of like reverse .map. In case of .prepend data transforms before the original event occurs and in the case of .map, data transforms after original event occurred.

Formulae#

const event = effect.prepend(fn)
  • When event is triggered, call fn with payload from event, then trigger effect with result of fn()

Arguments

  1. fn (Function): A function that receives payload, should be pure.

Returns

Event: New event.


map(fn)#

Creates a new event, which will be called after the original effect is called, applying the result of a fn as a payload. It is special function which allows you to decompose dataflow, extract or transform data.

Formulae#

const second = first.map(fn)
  • When first is triggered, pass payload from first to fn
  • Trigger second with the result of the fn() call as payload

Arguments

  1. fn (Function): A function that receives payload, should be pure.

Returns

Event: New event.

Example#

import {createEffect} from 'effector'
const userUpdate = createEffect(({name, role}) => {
console.log(name, role)
})
const userNameUpdated = userUpdate.map(({name}) => name) // you may decompose dataflow with .map() method
const userRoleUpdated = userUpdate.map(({role}) => role.toUpperCase()) // either way you can transform data
userNameUpdated.watch(name => console.log(`User's name is [${name}] now`))
userRoleUpdated.watch(role => console.log(`User's role is [${role}] now`))
await userUpdate({name: 'john', role: 'admin'})
// => User's name is [john] now
// => User's role is [ADMIN] now
// => john admin

Try it


use.getCurrent()#

Returns current handler of effect. Useful for testing.

Formulae#

fn = effect.use.getCurrent()

Returns

(Function): Current handler, defined by handler property or via use call.

Example#

const handlerA = () => 'A'
const handlerB = () => 'B'
const fx = createEffect(handlerA)
console.log(fx.use.getCurrent() === handlerA)
// => true
fx.use(handlerB)
console.log(fx.use.getCurrent() === handlerB)
// => true

Try it


Effect Properties#

note

You are not supposed to forward to parts of effect (even though it consists of events and stores), since it's a complete entity on its own. This behavior will not be supported

doneData#

Event, which is triggered with result of the effect execution:

Formulae#

event = effect.doneData
  • doneData is an event, that triggered when effect is successfully resolved with result from .done
Important

Do not manually call this event. It is event that depends on effect.

since

effector 20.12.0

Event triggered when handler is resolved.

Example#

import {createEffect} from 'effector'
const fx = createEffect(value => value + 1)
fx.doneData.watch(result => {
console.log(`Effect was successfully resolved, returning ${result}`)
})
await fx(2)
// => Effect was successfully resolved, returning 3

Try it

failData#

Event, which is triggered with error thrown by the effect

Formulae#

event = effect.failData
  • failData is an event, that triggered when effect is rejected with error from .fail
Important

Do not manually call this event. It is event that depends on effect.

since

effector 20.12.0

Event triggered when handler is rejected or throws error.

Example#

import {createEffect} from 'effector'
const fx = createEffect(async value => {
throw Error(value - 1)
})
fx.failData.watch(error => {
console.log(`Execution failed with error ${error.message}`)
})
fx(2)
// => Execution failed with error 1

Try it

done#

Event, which is triggered when handler is resolved.

Important

Do not manually call this event. It is event that depends on effect.

Properties#

Event triggered with object of params and result:

  1. params (Params): An argument passed to the effect call
  2. result (Done): A result of the resolved handler

Example#

import {createEffect} from 'effector'
const fx = createEffect(value => value + 1)
fx.done.watch(({params, result}) => {
console.log(
'Call with params', params,
'resolved with value', result,
)
})
await fx(2)
// => Call with params 2 resolved with value 3

Try it

fail#

Event, which is triggered when handler is rejected or throws error.

Important

Do not manually call this event. It is event that depends on effect.

Properties#

Event triggered with object of params and error:

  1. params (Params): An argument passed to effect call
  2. error (Fail): An error catched from the handler

Example#

import {createEffect} from 'effector'
const fx = createEffect(async value => {
throw Error(value - 1)
})
fx.fail.watch(({params, error}) => {
console.log(
'Call with params', params,
'rejected with error', error.message
)
})
fx(2)
// => Call with params 2 rejected with error 1

Try it

finally#

since

effector 20.0.0

Event, which is triggered when handler is resolved, rejected or throws error.

Important

Do not manually call this event. It is event that depends on effect.

Properties#

Event, which is triggered with object of status, params and error or result:

  1. status (string): A status of effect (done or fail)
  2. params (Params): An argument passed to effect call
  3. error (Fail): An error catched from the handler
  4. result (Done): A result of the resolved handler

Example#

import {createEffect} from 'effector'
const fetchApiFx = createEffect(async ({time, ok}) => {
await new Promise(resolve => setTimeout(resolve, time))
if (ok) return `${time} ms`
throw Error(`${time} ms`)
})
fetchApiFx.finally.watch(value => {
switch (value.status) {
case 'done':
console.log(
'Call with params',
value.params,
'resolved with value',
value.result
)
break
case 'fail':
console.log(
'Call with params',
value.params,
'rejected with error',
value.error.message
)
break
}
})
await fetchApiFx({time: 100, ok: true})
// => Call with params {time: 100, ok: true}
// resolved with value 100 ms
fetchApiFx({time: 100, ok: false})
// => Call with params {time: 100, ok: false}
// rejected with error 100 ms

Try it

pending#

Formulae#

$store = effect.pending
  • $store will update when done or fail are triggered
  • $store contains true value until the effect is resolved or rejected
Important

Do not modify $store value! It is derived store and should be in predictable state.

Example#

import React from 'react'
import {createEffect} from 'effector'
import {useStore} from 'effector-react'
const fetchApiFx = createEffect(
ms => new Promise(resolve => setTimeout(resolve, ms)),
)
fetchApiFx.pending.watch(console.log)
const Loading = () => {
const loading = useStore(fetchApiFx.pending)
return <div>{loading ? 'Loading...' : 'Load complete'}</div>
}
ReactDOM.render(<Loading />, document.getElementById('root'))
fetchApiFx(3000)

Try it

It's a shorthand for common use case

import {createEffect, createStore} from 'effector'
const fetchApiFx = createEffect()
//now you can use fetchApiFx.pending instead
const isLoading = createStore(false)
.on(fetchApiFx, () => true)
.on(fetchApiFx.done, () => false)
.on(fetchApiFx.fail, () => false)

inFlight#

Formulae#

$count = effect.inFlight
  • Store $count will be 0 if no calls of effect in pending state, its default state
  • On each call of effect state in $count store will be increased
  • When effect resolves to any state(done or fail) state in $count store will be decreased
Important

Do not modify $store value! It is derived store and should be in predictable state.

since

effector 20.11.0

Store which show how many effect calls aren't settled yet. Useful for rate limiting.

Example#

import {createEffect} from 'effector'
const fx = createEffect(() => new Promise(rs => setTimeout(rs, 500)))
fx.inFlight.watch(amount => {
console.log('in-flight requests:', amount)
})
// => 0
const req1 = fx()
// => 1
const req2 = fx()
// => 2
await Promise.all([req1, req2])
// => 1
// => 0

Try it

Last updated on