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.