In the interpreter branch, I've been implementing basic support for the equivalent of SCXML's <assign>, which is a way of modifying the data model in a very similar way to how you would modify a model in Redux.
Simple "counter" example of how this would work:
import { Machine, actions } from 'xstate';
const { assign } = actions;
const counterMachine = Machine(
{
initial: 'counting',
states: {
counting: {
on: {
INC: [
{
target: 'counting', // TODO: targetless transition
actions: [assign({ count: xs => xs.count + 1 })]
}
],
DEC: [
{
target: 'counting', // TODO: targetless transition
actions: [assign({ count: xs => xs.count - 1 })]
}
]
}
}
}
},
undefined,
{ count: 0 } // initial state
);
More details:
The default function signature for the assign() action creator is going to prefer object syntax, in order to improve determinism:
// โ
Prefer this:
assign({
count: xs => xs.count - 1,
foo: (_, e) => e.value
});
// โ ๏ธ Not this:
assign((xs, e) => ({
count: xs.count - 1,
foo: e.value
});
Bikeshedding time:
Currently (in the master branch) the State object has an .ext property that contains the extended state, so:
counterMachine.initialState.ext; // { count: 0 }
counterMachine
.transition(counterMachine.initialState, 'INC')
.ext; // { count: 1 }
Do you like the property name ext, or should we bikeshed it? Can't use value, data, and extendedState is too verbose (hence ext).
Also, should the initial extended state be specified as:
const counterMachine = Machine({ ... }, {
ext: { count: 0 }, // initial extended state
// ...
});
A few ideas:
I'm usually one to lean towards verbosity/readability over terseness and I don't care for abbreviations that leave the user trying to figure out what they could mean... ext is ok, but ext is used as an abbreviation for other things too, like, "extension"... I don't see an issue w/ extendedState...
That said, if looking for another option, I kind of like the term context because, its kind of like the context that machine is operating within. I.E. the machine can behave differently based on the context it has.
I was thinking of context as well, as it's a familiar term for referring to contextual data/extended state. And it helps that it ends in ext as well ๐
Let's get a few ๐ or ๐ for changing ext to context, but I'm leaning towards it.
Readability wins for me. We don't need to save bytes with short names any more - fortunately! ;-)
I'd vote for extState or extendedState.
The term "extended state" is already associated with state machines (e.g. on Wikipedia) so that name is more fitting than context that is more generic and vague.
For instance, if xstate would be used on a frontend with React, we'd have both xstate's context, and React's context.
@kamituel That's a good point (although, nothing is stopping you from using a state machine as actual context in React... that actually sounds like a good pattern!)
We're pretty locked into context right now, but it's only present in two areas (at most): the State instance (as state.context) and in the machine config (as context: ...), which you can optionally define as a third argument instead.
So your actions can still be written as:
const myAction = (extendedState, event) => {
// ...
}
to reduce confusion.
Closing this as it is now in the master branch, and in xstate@next
Most helpful comment
A few ideas:
I'm usually one to lean towards verbosity/readability over terseness and I don't care for abbreviations that leave the user trying to figure out what they could mean...
extis ok, butextis used as an abbreviation for other things too, like, "extension"... I don't see an issue w/extendedState...That said, if looking for another option, I kind of like the term
contextbecause, its kind of like the context that machine is operating within. I.E. the machine can behave differently based on thecontextit has.