menu
announcement

Spectrum will become read-only on August 10, 2021. Learn more about the decision in our official announcement.

Statecharts

The Statecharts community on Spectrum is (along with spectrum) MOVING TO OTHER PLATFORMS: For statecharts discussions in general go to Statecharts Discussion on GitHub (link) or Gitter (link). For XState-specific questions, go to the XState discussion forum for Q&A or the Stately discord chat to chat.

Channels
Team

Statecharts watercooler

March 7, 2019 at 5:55am
Show previous messages

August 2, 2020 at 9:31pm
I'm working with a machine that spawns children machines. In the "todos" example, the parent is configured to respond to messages the like of CHILD.EDIT. The corresponding actions uses the payload in the event to map through the children, and update accordingly when the right child id is found.
Instead, I would like to send messages "to" specific children using the ref handle I set for each child during the spawning process.
My initial attempts to parentService.send('EDIT', { to: child-id } seems to be falling on "deaf ears" :). It makes me wonder if spawn also calls start() for that child. Does it? What might I be missing here?
Thank you to anyone with a pointer.
Edited
  • reply
  • like

August 3, 2020 at 4:17pm
The { to: ... } option only works with send action creators, not with service.send(...) (sorry for the confusion). So your send('EDIT', { to: (ctx, e) => ... }) needs to be as an action, not via service.send(...).
  • reply
  • like
Ah, so that means given the service references the parent, then I must specify how to "pass down" messages from the parent to the child. To specify which child use the id spec used with the spawn command. Yes?
Given the following where the parent service has a context.children prop, is there a way to send messages directly to a specific child?
loading: {
entry: [
assign({
children: (ctx, e) => {
return ctx.children.map((child) => ({ // e.g., ctx.children = [1,2,3]
...child,
ref: spawn(
// child machine
childMachine.withContext(
{
...initialContext(mockGetView), // mock generating fn
id: child,
},
// handle to child machine
`childForm-${child}`,
),
),
}));
},
}),
log('...spawning child machines'),
log(
`sample child context: ${JSON.stringify(
initialContext(mockGetView),
)}`,
),
],
always: { target: 'editing' },
},
Edited
  • reply
  • like

August 18, 2020 at 12:10am
I don’t know why my brain isn’t working but is there an easy way to have a condition be checked after an action happens? Like when validating input? Like for example a cookie clicker like game:
  • reply
  • like
notEnoughCookies: {
on: {
EAT: [
{ target: 'haveEnoughCookies', cond: 'hasEatenEnoughCookies', actions: 'eatCookie' },
{ actions: 'eatCookie' }
]
}
},
haveEnoughCookies: { type: 'final' }
  • reply
  • like
The above doesn't work because there is an off by one error as eatCookie happens after hasEatenEnoughCookies guard
  • reply
  • like
Contrived example but maybe someone has an inspirational design idea that is better?
  • reply
  • like
I bet I either need intermediary states or the choose action
Edited
  • reply
  • like
notEnoughCookies: {
on: {
EAT: { target: 'checkCookies', actions: 'eatCookie' }
}
},
checkCookie: {
always: [
{ target: 'enoughCookies', cond: 'hasEnoughCookies' },
{ target: 'notEnoughCookies' }
]
},
enoughCookies: { type: 'final' }
I guess? seems a little verbose.
Edited
  • reply
  • like

August 18, 2020 at 1:15pm
It's verbose on purpose. There are two "states" in reality: the state where you executed the action, and the state where you are checking which state you should transition to based on that executed-action state.
  • reply
  • like
You can always make it less verbose by creating helper functions, since it's just a plain JS object. Nothing special there.
  • reply
  • like

August 20, 2020 at 8:49pm
So I have what I assume is a very typical use case, I have a modal flow where the user can choose to login, signup or click "forgot password". I model this as a statechart with two nested charts, one about the account creation process and another about the reset password flow (logging in is just part of the main chart)
  • reply
  • like
Since the account creation state is nested, I thought I would store context data in there (i.e. your personal data or whatever), however I can't seem to do that. It appears I am forced to have that context in the parent machine
  • reply
  • like
so the parent machine just has an initial state and can go to an "account_creation" state, which is the subchart. Inside the account_creation subchart, you can go back to the parent's "initial" state if you are in the account creation screen, but if you get past it, you can't go back anymore.
  • reply
  • like
const creation = {
initial: 'account_data',
context: {
// I want to read this
name: 'test',
some_data: 123
},
states: {
account_data: {
on: {
BACK: '#acct.initial',
NEXT: 'personal_data'
},
},
personal_data: {
on: {
NEXT: 'address'
}
},
address: {
on: {
BACK: 'personal_data',
NEXT: 'payment_data'
}
},
payment_data: {
on: {
BACK: 'address'
},
},
}
}
const machine = Machine({
id: 'acct',
initial: 'initial',
context: {
foo: 0
},
states: {
initial: {
on: {
CREATE: 'creation'
}
},
creation: {
on: {
NEXT: 'finish'
},
...creation
},
finish: {
type: "final"
}
}
});
  • reply
  • like
How can I get access to the child state? Do I have to pollute the main machine with that somewhat irrelevant state? Should I create separate machines (but then how do I go back)?
  • reply
  • like
or do I simply not use context at all, and instead use the local state of my component (I'm using react)
  • reply
  • like

October 16, 2020 at 10:51am
I was not able to understand how I can update context data by using external objects. Could some one please provide an example. I'm new to both React and xstate. I was not able to understand how to update the context
  • reply
  • like
Got it. We can update the data from the event...Thanks.
  • reply
  • like

October 27, 2020 at 10:19am
Hello everyone, I want to fetch data then update context every n seconds. But the code below doesn't work, any tips? thanks
activities: {
queryRoundStatus: (context) => {
const timer = setInterval(() => {
logger.log(`queryRoundStatus, inteval: ${HEART_BEAT_INTERVAL}`)
API.queryRoundStatus(context.gameId)
.then(data => assign({ ...data }))
}, HEART_BEAT_INTERVAL);
return () => clearInterval(timer)
},
},
  • reply
  • like
I think you would need to separate getting the data & saving the data to context. You could use a callback, then, when the callback fires, have a seperate action to save the response data to state. Here is an example of a callback that might help - https://bradwoods.io/guides/xstate/invoke-callback
  • reply
  • like
thanks, it works well.
  • reply
  • like
so I can only update context with actions, is that true?
  • reply
  • like
(simon-lu) ... more precisely, I believe I remember reading somewhere in the docs that the only way to update context is using actions.assign. The only other way I can think of is by "extending" an existing machine using withContext. I find that particularly useful when spawning child machines.
Edited
  • reply
  • like
(simon-lu) Try this:
invoke: {
src: () => (sendBack) => {
const interval = setInterval(() => sendBack({ type: 'HEARTBEAT' }), 1000)
return () => clearInterval(interval)
}
},
on: {
HEARTBEAT: { /* ... do whatever ... */}
}
  • reply
  • like
Show more messages