I'm trying to use Delayed Events
https://xstate.js.org/docs/guides/delays.html#delayed-events
I defined actions:
actions: {
reachedLevel: context => {
const { level, chosenLevel } = context;
if (level === chosenLevel) {
send('REACHED');
}
},
errorPressDirection: _context => {
//===== working
// setTimeout(() => {
// return send('REACHED');
// }, 1000);
//===== working
//send('REACHED')
// Not working
send('REACHED', { delay: 1000 });
},
},
If I use either the setTimeout or only send the type the code work well
setTimeout(() => {
return send('REACHED');
}, 1000);
Or
send('REACHED');
But send with option delay. This event is not running the REACHED
send('REACHED', { delay: 1000 });
I don't know is this my approach is wrong or delayed-events is not working.
I'm using version 4.7.8
Give me advice.
Thanks so much.
The
send(...)function is an action creator; it is a pure function that only returns an action object and does not imperatively send an event.
Also can you share the full code? I'm curious as to how it's working, as the send() action creator doesn't do anything, it only returns an object.
Hi @davidkpiano
This is my elevator Machine. Can you help me take a look at actions: errorPressDirection and activities: moving
Thanks so much.
const elevatorMachine = Machine(
{
id: 'elevator',
initial: 'stop',
context: {
level: 1,
chosenLevel: 1,
direction: '',
isDisablePressLevelBtn: true,
doorStatus: 'close',
},
states: {
stop: {
// current is stop the transition only can up or down
on: {
UP: {
target: 'up',
actions: assign({
direction: (context, event) => 'up',
isDisablePressLevelBtn: false,
doorStatus: 'open',
}),
},
DOWN: {
target: 'down',
actions: assign({
direction: (context, event) => 'down',
isDisablePressLevelBtn: false,
doorStatus: 'open',
}),
},
},
},
down: {
// current is down the transition only can up or stop
id: 'elevator_down',
on: {
PRESS_LEVEL: [
{
target: 'moving.go_down',
cond: 'pressLevelDownValidate',
actions: assign({
chosenLevel: (context, event) => +event.value,
doorStatus: 'close',
}),
},
{
target: 'error.go_down',
actions: assign({
chosenLevel: (context, event) => +event.value,
doorStatus: 'close',
}),
},
],
},
},
up: {
// current is up the transition only can down or up
id: 'elevator_up',
on: {
PRESS_LEVEL: [
{
target: 'moving.go_up',
cond: 'pressLevelUpValidate',
actions: assign({
chosenLevel: (context, event) => +event.value,
doorStatus: 'close',
}),
},
{
target: 'error.go_up',
actions: assign({
chosenLevel: (context, event) => +event.value,
doorStatus: 'close',
}),
},
],
},
},
error: {
states: {
go_up: {
entry: 'errorPressDirection',
on: {
REACHED: {
target: '#elevator_moving.go_down',
actions: assign({
direction: (context, event) => 'down',
}),
},
},
},
go_down: {
entry: 'errorPressDirection',
on: {
REACHED: {
target: '#elevator_moving.go_up',
actions: assign({
direction: (context, event) => 'up',
}),
},
},
},
},
},
moving: {
id: 'elevator_moving',
initial: 'finished',
states: {
go_up: {
activities: ['moving'],
entry: 'reachedLevel',
on: {
REACHED: 'reached',
MOVING: {
target: 'go_up',
actions: assign({
level: (context, event) => event.value + 1,
}),
cond: {
type: 'isMoveUp',
},
},
},
},
go_down: {
activities: ['moving'],
entry: 'reachedLevel',
on: {
REACHED: 'reached',
MOVING: {
target: 'go_down',
actions: assign({
level: (_context, event) => event.value - 1,
}),
cond: {
type: 'isMoveDown',
},
},
},
},
reached: {
entry: assign({
doorStatus: 'open',
}),
after: {
1000: 'finished'
}
},
finished: {
type: 'final',
},
},
onDone: {
target: 'stop',
actions: assign({
direction: (_context, _event) => '',
isDisablePressLevelBtn: true,
doorStatus: 'close',
}),
},
},
},
},
{
actions: {
reachedLevel: context => {
const { level, chosenLevel } = context;
if (level === chosenLevel) {
send('REACHED');
}
},
errorPressDirection: context => {
// setTimeout(() => {
// return send('REACHED');
// }, 1000);
send('REACHED', {
delay: 1000
});
},
},
activities: {
moving: (context, _event) => {
const { level } = context;
send('MOVING', {
value: level,
delay: 1000
});
// setTimeout(() => {
// return send({
// type: 'MOVING',
// value: level,
// });
// }, 1000);
},
},
guards: {
pressLevelDownValidate: (context, event, { cond }) => {
const chosenLevel = event.value;
const { level } = context;
return chosenLevel < level;
},
pressLevelUpValidate: (context, event, { cond }) => {
const chosenLevel = event.value;
const { level } = context;
return chosenLevel > level;
},
isMoveUp: (context, event, { cond }) => {
const { level, chosenLevel } = context;
return level < chosenLevel;
},
isMoveDown: (context, event, { cond }) => {
const { level, chosenLevel } = context;
return chosenLevel < level;
},
},
},
);
Hi @davidkpiano,
I really want to hear your opinion.
Please be patient - you can't expect to always get a response immediately. I bet David has seen your previous post or will get to it when he can. We have lives and we do this in our free time.
@Andarist yah, I know. I'm asking like a normal way. I don't rush. I just want to let him know that I shared the code and wanted to hear his opinion about my approach. It does not mean I'm asking he have to answer asap and I don't mean disturb his life. The difference timezone is one more reason.
And I apologize if either you or he feel frustrating
@mymai91 Great elevator example, can you put this in a CodeSandbox?
Also, the activity should be considered fire-and-forget, so what you really want here is to invoke a service:
services: {
moving: (context, _event) => (callback) => {
const { level } = context;
callback('MOVING', {
value: level,
delay: 1000
});
},
},
And invoke it this way:
states: {
go_up: {
invoke: ['moving'],
// ...
And that reachedLevel should be most likely refactored to a guarded transient transition, smth like:
on: {
'': { cond: 'reachedLevel', target: 'reached' }
}
@davidkpiano
Thanks for your answer. I think, invoke a service is exactly what I need but the only thing is it might not support delay (I read the document and see that https://xstate.js.org/docs/guides/communication.html#invoking-callbacks)
I still using setTimeout for my purpose because I want the level_value slowdown transition 1 second.
This is my demo: https://codesandbox.io/s/thirsty-rain-gxgo0
Andarist: Thanks for your comment. I tried refactored as you suggest but it's not working.
Thanks so much for helping me solve my problem. ^___^
@mymai91 No problem; feel free to ask in spectrum.chat/statecharts so we can help you further.