Accessing an event.sender inside of onDone
#5461
-
|
I'm hoping someone can help me with a problem I'm running into. I'm following the example of sending a response from one invoked actor to another and don't see an obvious way to resolve my issue. This example is taken directly from the docs but tweaked slightly to help illustrate my issue/question. What is the best approach to be able to get access to the value import { assign, createMachine, fromPromise, sendTo } from '../../../xstate';
const fetchUser = (userId: string) => fetch(`/api/users/${userId}`).then((response) => response.json());
const authServerMachine = createMachine({
id: 'server',
initial: 'waitingForCode',
context: {
user: {} as { id: string; token: string },
},
states: {
waitingForCode: {
on: {
CODE: {
// I need to make an async call here and wait for response to send back to client
// instead of just immediately replying
target: 'fetchCode',
//actions: sendTo(({ event }) => event.sender, { type: 'TOKEN' }, { delay: 1000 }),
},
},
},
fetchCode: {
invoke: {
id: 'get-code',
src: fromPromise(({ input }) => fetchUser(input.userId)),
input: ({ event }) => {
// event.sender is available here, but I don't see a way to "save" it for use in
// onDone from here
return event;
},
onDone: {
actions: [
// save to context
assign({
user: ({ event }) => event.output,
}),
// send token back to client
({ event, context }) => {
// I have no access to event.sender here
// event is the response from fetchUser
// how can I access the event data from the original request?
sendTo(event.sender, { type: 'TOKEN', token: context.user.token });
},
],
},
},
},
},
});
const authClientMachine = createMachine({
id: 'client',
initial: 'idle',
states: {
idle: {
on: {
AUTH: { target: 'authorizing' },
},
},
authorizing: {
invoke: {
id: 'auth-server',
src: authServerMachine,
},
entry: sendTo('auth-server', ({ self }) => ({
type: 'CODE',
userId: 'user-123',
sender: self,
})),
on: {
TOKEN: { target: 'authorized' },
},
},
authorized: {
type: 'final',
},
},
});Is there another way to do this without sacrificing the potential reusability of the this is just a contrived example, so let's assume I want to keep the basic idea provided above |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
|
Great question - and yeah sorry about losing access to The "cleanest" v5 approach is exactly what you suspected: store the sender reference in const authServerMachine = createMachine({
id: 'server',
initial: 'waitingForCode',
context: {
user: {} as { id: string; token: string },
replyTo: null as ActorRef<any, any> | null,
},
states: {
waitingForCode: {
on: {
CODE: {
target: 'fetchCode',
actions: assign({
replyTo: ({ event }) => event.sender,
}),
},
},
},
fetchCode: {
invoke: {
id: 'get-code',
src: fromPromise(({ input }) => fetchUser(input.userId)),
input: ({ event }) => ({ userId: event.userId }),
onDone: {
actions: [
assign({
user: ({ event }) => event.output,
}),
sendTo(
({ context }) => context.replyTo!,
({ context }) => ({ type: 'TOKEN', token: context.user.token })
),
],
},
},
},
},
});This is actually pretty idiomatic in the actor model: storing a "reply-to" address is how Akka, Erlang, and other actor systems handle this too. XState v5 doesn't have a built-in FWIW, this is something we're improving in the upcoming v6 with state params 🎉 you'll be able to pass data to a target state directly w/o polluting context: CODE: ({ event }) => ({
target: 'fetchCode',
params: { sender: event.sender },
}),
// ...
fetchCode: {
invoke: {
// ...
onDone: ({ params, output }, enq) => {
enq.sendTo(params.sender, { type: 'TOKEN', token: output.token });
},
},
},This keeps transient data like |
Beta Was this translation helpful? Give feedback.
Great question - and yeah sorry about losing access to
senderthrough the transitions; that's how it currently works in v5.The "cleanest" v5 approach is exactly what you suspected: store the sender reference in
context. It's not as messy as you might think - actor references are lightweight and designed to be passed around. There shouldn't be a memory leak concern (please file a bug if you see one; I'll also double-check).