Xstate <> React <> TypeScript integration snippets #3114
Replies: 4 comments
-
The Or you can do |
Beta Was this translation helpful? Give feedback.
-
@davidkpiano @AleBlondin import React from 'react';
import { Interpreter, State } from 'xstate';
export interface RootMachineContext {
id: string;
}
interface RootMachineToggleEvent {
type: 'TOGGLE';
id: string;
}
interface RootMachineScanEvent {
type: 'SCAN';
}
export type RootMachineEvent =
| RootMachineToggleEvent
| RootMachineScanEvent;
export const StateContext = React.createContext<
[
State<RootMachineContext, RootMachineEvent>,
Interpreter<RootMachineContext, any, RootMachineEvent>['send'],
Interpreter<RootMachineContext, any, RootMachineEvent>,
]
>([
{} as State<RootMachineContext, RootMachineEvent>,
((() => {}) as any) as Interpreter<
RootMachineContext,
any,
RootMachineEvent
>['send'],
{} as Interpreter<RootMachineContext, any, RootMachineEvent>,
]); |
Beta Was this translation helpful? Give feedback.
-
so far the following seems to work well for me when defining types for a machine: type StateContext = {}
type StateEvent = { type: 'SOME_EVENT' }
type StateSchema = { states: { some_state: {} } }
type TypeState = Distribute<keyof StateSchema["states"], StateContext> // assuming same context
type Context = [
State<StateContext, StateEvent, StateSchema, TypeState>,
Interpreter<StateContext, StateSchema, StateEvent, TypeState>['send']
];
type Distribute<U, C> = U extends any ? { value: U; context: C } : never // util
const AppContext = React.createContext<Context>({} as any); note the addition of |
Beta Was this translation helpful? Give feedback.
-
I just happened to find an interim solution for this by using constate. function usePetFormMachine() {
return useMachine(petFormMachine)
}
export const [PetFormMachineProvider, usePetFormMachineContext] = constate(
usePetFormMachine,
)
function SomeComponent() {
const [current, send] = usePetFormMachineContext()
return current.value
}
function App() {
return (
<PetFormMachineProvider>
<SomeComponent />
</PetFormMachineProvider>
}
} I know that using a utility library is not what this issue is about though. Just wanted to share in case it could help someone avoid the extensive type ritual while we have a more definite answer :D |
Beta Was this translation helpful? Give feedback.
-
Bug or feature request?
Documentation / feature proposal
Description:
Hi, I just implemented my first state machine on my React/TypeScript project with xstate ! I followed the documentation but I faced some issues. I would like to present some technical solutions and code snippets that I wrote to overcome those issues:
If they are relevant I would be happy to open a PR :) Otherwise I welcome any feedback.
Using React context to share a state machine
I created the state machine at a quite high level and I didn't want to drill props through 4-5 components, so I figured out I would put the machine in React context. However I found it quite painful to type the context.
Initially the context is null so the type can't be inferred automatically by TypeScript. I created this utils to create the context:
Consuming the context
When consuming the React context, I faced another issue: TypeScript indicates that state and send properties are not always defined (because React context value is initially null).
This implies that I have to check for nullity every single time, even though I know I initialized the state machine higher up in the component tree with a useMachine hook. This is the utils I wrote:
Because the hook throws an error when context is not defined, TypeScript can narrow the state and send type from
SomethingType || undefined
toSomethingType || never
which is equivalent toSomethingType
Thanks for reading !
Beta Was this translation helpful? Give feedback.
All reactions