Feature + Discussion (it's possible I'm missing something here)
The data option for invoke does not seem to provide a way of only overriding some of the child's context while leaving others intact.
data: (parentContext, event, childContext) => ({
...childContext,
foo: "bar"
})
I've been thinking about this with normal machine configs too... that is, machine.withContext({...}) should also shallowly merge, and that behavior should be customizable.
As a workaround, you can still do this:
src: childMachine,
data: {
...childMachine.context, // initial context
foo: 'bar'
}
yeah, but then you can't use the strings version :~(
What do you mean?
src: "childMachine"
Real-world scenarios probably wouldn't be a deal-breaker since I'd imagine config and options are usually created in something like the same scope, so one could do like options.services.childMachine.context ... but it feels a little odd
I know. I'll add shallow-merging in the next minor version.
One question on this. If I wanted to _delete_ keys on context Iād still need the existing api.
This makes me question if just keeping the existing api with workaround is more consistent for both usecase.
I could have also just misunderstood everything and we should all just ignore this comment.
: M
This makes me question if just keeping the existing api with workaround is more consistent for both usecase.
Yeah, that's valid. The "workaround" is just normal JavaScript, which is why I'm still hesitant to add magical "shallow-merging" as default behavior.
what about the function approach, i.e. passing the child context as the third param? It's backwards compatible too...
I donāt know from a consistency standpoint with similar APIās in the package but from a functional point itās not a bad idea. I can even see how one could create a reusable factory/function to generate little āmerge functionsā that can be reused across the app.
I donāt know from a consistency standpoint with similar APIās in the package
And since this would be quite a reusable pattern, are there other/similar functions in the current API that could benefit from a callback parameter with this signature? Seeing as it could/would promote a more functional approach to similar problems.
The functional approach is available here already, I'm just proposing to add the child context as an additional param :)
Indeed ;) Then the question: are there other callback functions that could benefit from having previous value passed to them, or functions where adding a callback function with a previous value signature would make sense to have a āconsistentā API.
But if it is as you say that itās just about adding another param to an existing callback I donāt see a problem with that.
(then again Iām answering this from my phone without even having looked at any source code, so Iād take anything I say with a degree of caution).
Oh, that's an interesting perspective... I wasn't thinking of it as much as "previous value" more like this specific case of "child value", but not sure how it's actually implemented and stepping backwards through a context history would be really useful (though I guess you could do it manually by storing some cache on the context)
Hehe - yeah, I've been prefacing like all my questions lately with "I'm not sure if I'm just missing something here, but..." :D
Keep in mind that the thing you're invoking can be completely opaque. It's not always something you have direct reference to, like a local machine. It can be a remote machine, where it might not even be _possible_ to know what the "child context" is ahead of time.
As a rule of thumb, always pretend that invoked services are akin to a 3rd-party REST API (or some other remote service). It's impossible to know the "child context" of that API resource without querying for it first.
After thinking about this for a while, I'm going to hold back from doing auto-merge, especially for the use-case of wanting certain properties to be undefined (merging would give unexpected behavior in that instance).
For both root machines and invoked child machines, the merging pattern should be this:
invoke: {
src: childMachine,
data: (ctx, e) => ({
...childMachine.context, // initial context
foo: 'bar'
})
}
It is a straightforward, idiomatic JS solution that doesn't require any magic. š
Just stumbled upon this issue. My child machine has some internal context and some context props that will be set from the parent machine.
I think it's fine the way you propose.
Another way would be to initialize the child machine, setting the internal context after context is set from outside:
{
id: 'child',
initial: 'initialize',
states: {
initialize: {
onEntry: assign({
some: false,
internal: true,
context: 1
}),
on: {
'': 'idle'
}
},
idle: {}
}
}
I'm curious what you think about this solution.