Patterns for sharing a machine as a lib #1495
-
Quick question: what are the best practices / patterns for sharing machines / actors? I have a small machine to do exponential backoff requests, and I'd like to share it in a library. I don't know how users will use it - if directly interpreting it, invoking it or spawning it as an actor. I thought of just using In the machine definition: // ...
success: {
entry: "updateSuccess",
type: "final",
},
failure: {
entry: "updateFailure",
type: "final",
},
waiting: {
entry: "updateRetry",
after: {
DELAY: {
target: "started",
},
},
},
// ... In the machine config: // ...
actions: {
updateFailure: noop,
updateSuccess: noop,
updateRetry: noop,
incrementAttempts: assign({
attempts: (context) => context.attempts + 1,
}),
// ...
function noop() {
return null;
} And finally if someone consumes it as an actor: const machine = createMachine(failingPromiseFactory).withConfig({
actions: {
updateFailure: sendParent((context) => ({
type: "FAILURE",
attempts: context.attempts,
errors: context.errors,
})),
updateSuccess: sendParent((context) => ({
type: "SUCCESS",
attempts: context.attempts,
errors: context.errors,
result: context.result,
})),
updateRetry: sendParent("RETRY"),
}, But that seems quite verbose. I was wondering if there's a better pattern to share a machine (or a collection of machines) in a lib, retaining the flexibility of it being used directly or as an actor. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 1 reply
-
This has me thinking that maybe Anyway, is your plan to have this consumable as an XState machine, or a general-purpose utility? As an XState machine: Use As a general-purpose utility: You can try this pattern: export function exponentialBackoff(getPromise, { onSuccess, onFailure, onRetry }) {
const machine = createMachine(/* ... */)
.withConfig({
actions: { onSuccess, onFailure, onRetry }
});
interpret(machine).start();
} Which can be generally consumed like this: import { exponentialBackoff } from '...';
exponentialBackoff(() => fetch('some/api'), {
onSuccess: res => console.log(res),
onFailure: res => console.error(res),
// ...
}); |
Beta Was this translation helpful? Give feedback.
-
@davidkpiano thanks for the quick response.
Both. I would like to export the same machine to be
I guess that, with that, it would really handy to write standalone machines that duplicate as actors with the same code... |
Beta Was this translation helpful? Give feedback.
-
Thanks for your response, @Andarist
Why not? If I write a machine with
But I guess they wouldn't need to do that if |
Beta Was this translation helpful? Give feedback.
-
And how exactly they would listen to those actions? Yes, it's doable - but I would consider this to be quite cumbersome as they would have to "decode" that information from what |
Beta Was this translation helpful? Give feedback.
This has me thinking that maybe
sendParent
shouldn't throw if there is no parent, but rather gracefully continue...Anyway, is your plan to have this consumable as an XState machine, or a general-purpose utility?
As an XState machine:
Use
sendParent
, as you have described above. The assumption is that this machine is to be invoked.As a general-purpose utility:
You can try this pattern:
Which can be generally consumed like this: