Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Add warning and stop forwarding when forwarding to undefined #3178

Conversation

davidkpiano
Copy link
Member

@davidkpiano davidkpiano commented Mar 26, 2022

This PR:

  • Adds the optional sendType property to send actions to tell what "kind" of send action it is (plain send, forwarding)
  • Prevents targeting undefined for forwardTo, which will prevent an infinite loop (which is already prevented in the types)
  • Shows a warning when attempting to forward to undefined

@changeset-bot
Copy link

changeset-bot bot commented Mar 26, 2022

🦋 Changeset detected

Latest commit: 3f265c0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
xstate Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@davidkpiano davidkpiano linked an issue Mar 26, 2022 that may be closed by this pull request
@codesandbox-ci
Copy link

codesandbox-ci bot commented Mar 26, 2022

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 3f265c0:

Sandbox Source
XState Example Template Configuration
XState React Template Configuration

@ghost
Copy link

ghost commented Mar 26, 2022

👇 Click on the image for a new way to code review
  • Make big changes easier — review code in small groups of related files

  • Know where to start — see the whole change at a glance

  • Take a code tour — explore the change with an interactive tour

  • Make comments and review — all fully sync’ed with github

    Try it now!

Review these changes using an interactive CodeSee Map

Legend

CodeSee Map Legend

it('should not cause an infinite loop when forwarding to undefined', () => {
const machine = createMachine({
on: {
'*': { cond: () => true, actions: forwardTo(undefined as any) }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what was causing the infinite loop...

false,
`Attempted to forward event "${sendAction.event.type}" to undefined actor`
);
break;
} else {
this.send(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the infinite loop was being caused due to this line, which sends the event to self if sendAction.to is undefined.

@@ -872,6 +872,13 @@ export class Interpreter<
} else {
if (sendAction.to) {
this.sendTo(sendAction._event, sendAction.to);
} else if (sendAction.sendType === 'forward') {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, when we're forwarding an event, it should never be forwarded to self! So we need a way of identifying that the send action is a "forwarding" action, without breaking any behavior (hence sendAction.sendType === 'forward')

@@ -563,7 +564,8 @@ export function forwardTo<TContext, TEvent extends EventObject>(
): SendAction<TContext, TEvent, AnyEventObject> {
return send<TContext, TEvent>((_, event) => event, {
...options,
to: target
to: target,
sendType: 'forward'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To do this, we add this little piece of sendType: 'forward' metadata to the action object created by forwardTo(...)

@@ -205,7 +205,8 @@ export function send<
? options.id
: isFunction(event)
? event.name
: (getEventType<TSentEvent>(event) as string)
: (getEventType<TSentEvent>(event) as string),
sendType: options?.sendType
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure we're adding this on the action object...

const service = interpret(machine).start();

// This is testing for an infinite loop
service.send('TEST');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And all should be well! Without adding that metadata, this would cause an infinite loop and this test would timeout (or worse, just hang).

This test completing is the success criterion.

@davidkpiano
Copy link
Member Author

Click "Tour" to see this code tour in CodeSee

CleanShot 2022-03-26 at 10 07 00@2x

@Andarist Andarist merged commit 6badd2b into main Jul 6, 2022
@Andarist Andarist deleted the 3177-footgun-forwardto-causes-infinite-loop-when-you-pass-undefined branch July 6, 2022 17:57
@github-actions github-actions bot mentioned this pull request Jul 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Footgun: forwardTo causes infinite loop when you pass undefined
2 participants