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

[v5] setup() API #4353

Merged
merged 13 commits into from
Nov 21, 2023
Merged

[v5] setup() API #4353

merged 13 commits into from
Nov 21, 2023

Conversation

davidkpiano
Copy link
Member

@davidkpiano davidkpiano commented Oct 15, 2023

const getChatCompletion = fromPromise(async () => { ... });
const processResult = fromPromise(async () => { ... });
const sendToDiscord = fromPromise(async () => { ... });

const machine = setup({
  actors: {
    getChatCompletion,
    processResult,
    sendToDiscord
  }
}).createMachine({
  // ...
  states: {
    thinking: {
      invoke: { src: 'getChatCompletion', onDone: 'processing' }
    },
    processing: {
      invoke: { src: 'processResult', onDone: 'sending' }
    },
    sending: {
      invoke: { src: 'sendToDiscord', onDone: 'done' }
    },
    done: { type: 'final' }
  }
});

@changeset-bot
Copy link

changeset-bot bot commented Oct 15, 2023

🦋 Changeset detected

Latest commit: 3bd3767

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

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

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

}
});

p.createMachine({
Copy link
Member Author

@davidkpiano davidkpiano Oct 15, 2023

Choose a reason for hiding this comment

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

This fails without setup()

Copy link
Member Author

Choose a reason for hiding this comment

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

types: {
  children: {} as {
    [key: `todo-${number}`]: 'todo';
    ['my-notifier']: 'notifierSrc';
  }
}

Or:

types: {
  childrenIds: {} as {
    todo: `todo-${number}`,
    notifierSrc: 'my-notifier',
  }
}

@codesandbox-ci
Copy link

codesandbox-ci bot commented Oct 15, 2023

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 3bd3767:

Sandbox Source
XState Example Template Configuration
XState React Template Configuration

@davidkpiano davidkpiano changed the title [v5] provide() API [v5] setup() API Oct 18, 2023
@davidkpiano davidkpiano marked this pull request as ready for review October 27, 2023 13:30
@nstadigs
Copy link

nstadigs commented Nov 2, 2023

Big fan of this change! Would it be weird to also support setting the event type in setup()? A framework could then use setup to export a fully preconfigured createMachine function where all the possible events and side effects are provided beforehand without using type hacks, no? See this discussion for context: #4315

@Andarist
Copy link
Member

Andarist commented Nov 2, 2023

Big fan of this change! Would it be weird to also support setting the event type in setup()?

That's the current plan.

A framework could then use setup to export a fully preconfigured createMachine function where all the possible events and side effects are provided beforehand without using type hacks, no? See this discussion for context: #4315

I'm not sure if that would solve this particular issue though. The event type could be "preconfigured" here but other than that it would be set in stone. IIRC, you wanted to extend it with more event types.

@with-heart
Copy link
Contributor

Don't forget to add setup to the core exports 😆

@with-heart
Copy link
Contributor

Hey @davidkpiano, could you expand the example in the PR description to show envisioned usage for the other keys of the setup object param?

I really like the idea of defining the implementations up front and deriving types from them as much as possible but I'm struggling with thinking through how this could work with built-in actions like assign.

@davidkpiano
Copy link
Member Author

Hey @davidkpiano, could you expand the example in the PR description to show envisioned usage for the other keys of the setup object param?

I really like the idea of defining the implementations up front and deriving types from them as much as possible but I'm struggling with thinking through how this could work with built-in actions like assign.

Hey sure!

So, you can always inline assign(...). But if you want to "serialize" them:

const machine = setup({
  actions: {
    setToZero: assign({ 
      count: 0
    })
  }
}).createMachine({
  entry: 'setToZero'
});

Not sure how context typings will work, or if/how parameters will work with this yet (cc. @Andarist)

@Andarist
Copy link
Member

This one works just fine with what I will be pushing out soon:

setup({
  types: {} as {
    context: {
      count: number;
    };
  },
  actions: {
    resetTo: assign((_, params: number) => ({
      count: params
    }))
  }
})

@nstadigs
Copy link

Big fan of this change! Would it be weird to also support setting the event type in setup()?

That's the current plan.

A framework could then use setup to export a fully preconfigured createMachine function where all the possible events and side effects are provided beforehand without using type hacks, no? See this discussion for context: #4315

I'm not sure if that would solve this particular issue though. The event type could be "preconfigured" here but other than that it would be set in stone. IIRC, you wanted to extend it with more event types.

In my case the file providing the preconfigured 'createMachine' (using setup) would be generated, so extra event types can be specified before generating the code.

…r order guards (#4474)

* Rework setup inference and specifically guard inferences to aid higher order guards

* move tests

* add extra runtime tests for guards execution

* fixed `not` types

* shift one extra type-level test

* Remove `TParams` from `choose` and `pure`
@Andarist Andarist merged commit a3a11c8 into next Nov 21, 2023
2 checks passed
@Andarist Andarist deleted the v5/provide branch November 21, 2023 12:20
@Necmttn
Copy link

Necmttn commented Nov 23, 2023

THANK YOU for this! This cleared up quite a bit type inference I was doing.

@joernroeder
Copy link

joernroeder commented Nov 29, 2023

I'm just stumbling upon this from within the changelog. can someone give more context what this replaces or what the benefits of writing it that way are? is it just the call signature of createMachine? is it Typescript? what's the difference to myMachine.provide({ actors: { myActor } }}

thanks in advance

@davidkpiano
Copy link
Member Author

I'm just stumbling upon this from within the changelog. can someone give more context what this replaces or what the benefits of writing it that way are? is it just the call signature of createMachine? is it Typescript? what's the difference to myMachine.provide({ actors: { myActor } }}

thanks in advance

By setting up the types in advance, not only is it much less boilerplate, but with how TypeScript works, we're able to more strongly infer typing throughout the machine. So instead of e.g.:

createMachine({
  types: {
    actors: {} as {
      logic: typeof someLogic;
    }
  }
}).provide({
  actors: { someLogic }
})

You can write:

setup({
  actors: { someLogic }
}).createMachine({/* ... */});

Both setup() and .provide() can be used together; they're for different use-cases:

  • setup() is for setting up the known implementations (or stubs) in advance
  • .provide() is for providing implementations to override the existing ones.

@joernroeder
Copy link

Thanks @davidkpiano for the swift response and clarification, love it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants