Skip to main content

waitfor

Sometimes when doing Server Side Rendering (SSR) or testing your logic, you might want to await for an action. This is what the kea-waitfor plugin does!

Keep In Mind

kea-waitfor in designed for use in tests (and is surpassed by kea-test-utils), or in a Server Side Rendering context. It is not designed for daily use in listeners and elsewhere, as it's easy to miss dispatched events that happen synchronously. You have the danger of edge cases where you'll be waiting for an event that already passed.

Installation

First install the kea-waitfor package:

# if you're using yarn
yarn add kea-waitfor

# if you're using npm
npm install --save kea-waitfor

Then install the plugin:

import { waitForPlugin } from 'kea-waitfor'
import { resetContext } from 'kea'

resetContext({
plugins: [
waitForPlugin({
/* options */
}),
],
})

waitForAction

To wait for a kea action, use waitForAction:

import { waitForAction } from 'kea-waitfor'

const payload = await waitForAction(logic.actionTypes.myAction)

For example:

import { kea } from 'kea'
import { waitForAction } from 'kea-waitfor'

const logic = kea([
actions({
setValue: (value) => ({ value }),
valueWasSet: (value) => ({ value }),
}),

listeners(({ actions }) => ({
setValue: async ({ value }) => {
await delay(300)
actions.valueWasSet(value)
},
})),
])

logic.mount()
logic.actions.setValue('hamburger')
const { value } = await waitForAction(logic.actionTypes.valueWasSet)

console.log(value)
// --> 'hamburger'

waitForCondition

To wait for any random condition, use waitForCondition:

import { waitForCondition } from 'kea-waitfor'

const payload = await waitForCondition((action) => action.payload.id !== 'new')

For example:

import { waitForCondition } from 'kea-waitfor'

const logic = kea([
actions({
setValue: (value) => ({ value }),
valueWasSet: (value) => ({ value }),
}),

listeners(({ actions }) => ({
setValue: async ({ value }) => {
await delay(300)
actions.valueWasSet(value)
},
})),
])

logic.mount()
logic.actions.setValue('cheeseburger')
const { value } = await waitForCondition((action) => {
return (
action.type === logic.actionTypes.valueWasSet && action.payload.value === 'cheeseburger'
)
})

console.log(value)
// --> 'cheeseburger'

Wait for many events

All events finish

To wait for multiple actions to finish, use Promise.all like you would with other promises:

Promise.all([
waitForAction(logic.actions.valueWasSet),
waitForAction(logic.actions.clickedButton),
]).then(([valueWasSetPayload, clickedButtonPayload]) => {
console.log(valueWasSetPayload, clickedButtonPayload)
})

First event

To wait for the first action to finish, use Promise.race:

Promise.race([
waitForAction(logic.actions.settingValueSuccess),
waitForAction(logic.actions.settingValueFailure),
]).then((firstPayload) => {
console.log(firstPayload) // but which one?
})

First event with metadata

To add more metadata to better detect the winning action, feel free to add .then(...) to the promises:

Promise.race([
waitForAction(logic.actions.settingValueSuccess).then((payload) => ({
success: true,
...payload,
})),
waitForAction(logic.actions.settingValueFailure).then((payload) => ({
success: false,
...payload,
})),
]).then(({ success }) => {
console.log(success)
})

With a timeout

To wait with a timeout, use a makeshift timebomb:

const delay = (ms) => new Promise((resolve) => window.setTimeout(resolve, ms))

Promise.race([
waitForAction(logic.actions.settingValueSuccess).then((payload) => ({
status: 'ok',
...payload,
})),
waitForAction(logic.actions.settingValueFailure).then((payload) => ({
status: 'error',
...payload,
})),
delay(5000).then(() => ({ status: 'timeout' })),
]).then(({ status }) => {
console.log(status)
})

Questions & Answers

Ask questions about this page here.