Xstate: escalate() should allow a function as a parameter to pass an actual error event instead of just a custom error object

Created on 1 Jan 2020  路  3Comments  路  Source: davidkpiano/xstate

Bug or feature request?

Feature Request

Description:

With the release of xstate 4.7 we now have a new function called as escalate() which basically is used to send an action of type error back to the parent.

This is very useful when we invoke a machine and if that machine leads to onError, we needed a way to inform its parent about the same.

A discussion around this has already happened here
https://github.com/davidkpiano/xstate/issues/696

The resolution provided was this
https://xstate.js.org/docs/guides/actions.html#escalate-action

Now with this, the problem is if let's say a parent machine invokes another machine and that child machine invokes a promise.
When this promise is rejected it lands into the onError handler on the child machine.
With escalate I can inform the parent machine about this error with a custom error object, but I have no way to send the exact error event that occurerd.

For example currently we can do

escalate({ message: "some error occurred" });

What will be more ideal here is if we could do something like this

escalate((context, event) => ({
    event,
    message: 'some error occured'
    ... this could be anything the user wants to send back to the parent
}));

escalate should accept a function also which will have context and event passed to it.

(Feature) Potential implementation:

Required! If you would like to see a feature or enhancement make its way to xstate, please describe:

  • What the API would look like
    The API can look something like this
escalate((context, event) => ({
    event,
    message: 'some error occured',
    ... this could be anything the user wants to send back to the parent
}));

So it can allow an object, a string or a function.

Link to reproduction or proof-of-concept:

You can use CodePen or CodeSandbox template to create a reproduction/POC.

enhancement

Most helpful comment

It is weird that this was not included in the original design of escalate...

Here's a workaround for anyone needing this functionality now (like me):

import { actionTypes } from "xstate/lib/actions";

// ...

entry: sendParent((context, event) => ({
  type: actionTypes.error,
  data: {
    // Any properties for data
  },
})),

All 3 comments

Not sure if this is the right approach to solve this but I have certain implementation in mind.

/**
 * Escalates an error by sending it as an event to this machine's parent.
 *
 * @param escalatedError The error data to send or error fn which will return the error data to send
 * @param options Options to pass into the send action creator.
 */
export function escalate<TContext, TEvent extends EventObject>(
  escalatedError: Mapper<TContext, TEvent> | object | any,
  options?: SendActionOptions<TContext, TEvent>
): SendAction<TContext, TEvent> {
  return sendParent<TContext, TEvent>(
    {
      type: actionTypes.error,
      data: escalatedError
    },
    {
      ...options,
      to: SpecialTargets.Parent
    }
  );
}

Then inside actions.ts we can have a check like this

if (resolvedEvent.name === actionTypes.error) {
    isFunction(resolvedEvent.data.data) {
      resolvedEvent.data.data = resolvedEvent.data.data(ctx, _event.data);
    }
  }

As I am no expert in this, it would be great if someone could help me out here.

@davidkpiano any suggestions around this approach or any other way that I should approach this.
I would be happy to send a PR.

It is weird that this was not included in the original design of escalate...

Here's a workaround for anyone needing this functionality now (like me):

import { actionTypes } from "xstate/lib/actions";

// ...

entry: sendParent((context, event) => ({
  type: actionTypes.error,
  data: {
    // Any properties for data
  },
})),
Was this page helpful?
0 / 5 - 0 ratings