npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

xstate-react-hoc

v1.0.1

Published

A React HOC for implementing xstate machines inside your react components.

Readme

xstate-react-hoc

A React HOC for implementing xstate machines inside your react components.

Travis (.org)

Codecov

GitHub repo size

NPM

npm

GitHub top language

GitHub last commit

Description

The library aims to offer a React HOC in order to associate xstate machines with your React Component, injecting different props that can be used for interacting with xstate.

Usage

In order to create your HOC you can use the following function:

function withStateMachine<
  TContext,
  TStateSchema extends StateSchema,
  TEventObject extends EventObject,
  P extends WithStateMachineProps<TContext, TEventObject>
>(
  Comp: ComponentType<P>,
  machineConfig: MachineConfig<TContext, TStateSchema, TEventObject>,
  machineOptions?: Partial<MachineOptions<TContext, TEventObject>>,
): HOCWithStateMachineType<TContext, TEventObject, P>

This HOC will inject the following props into the component:

export interface WithStateMachineProps<TContext, TEvent extends EventObject> {
  machineState: State<TContext, TEvent>;
  machineContext: TContext;
  machineEvent: OmniEventObject<TEvent>;
  sendEvent: (event: TEvent) => void;
  extendConfig: (opts: Partial<MachineOptions<TContext, TEvent>>, ctx?: TContext) => void;
}

Example

Inside the repository on github, it is present a full example which shows how to create a custom xstate machine, for toggling visibility and changing content of components, plus how to inject it through the HOC to all the components, with then the implementation of the component for using the injected state machine context inside the component state.

As first thing, let's define what will be the states of our machine, for accomplishing the tasks described above:

  • Hiding/Showing
  • Changing the content.

We can then identify that our state machine should have two single states:

  • Hidden: From this state it reacts to two different events:
    • TOGGLE: For being visible
    • SET_TEXT: For changing its content, because we may want that the content can change also when it is not visible, for showing then the different content when it will be visible again. If you want instead to not change the content when the component is not visible, you can not react on this event when the machine is in the Hidden state.
  • Visible: It reacts to the same events as above, in order to change the visibility and become hidden, and to change its content.

Let's define then two Enums which holds our states and our events, used for applyging a transition from one state into the other:

export enum AppMachineState {
  HIDDEN = 'HIDDEN',
  VISIBLE = 'VISIBLE'
}

export enum AppMachineEvent {
  TOGGLE = 'TOGGLE',
  SET_TEXT = 'SET_TEXT'
}

Let's then define the events that can be dispatched to this state machine.

In order to toggle the visibility, we do not need any other additional data to be passed to the machine, instead for setting the content we need to pass the new content that we want to set.

We can then define the following interfaces representing our event objects, plus a generic interface for representing all the events of the machine:

export interface AppMachineToggleEvent {
  type: AppMachineEvent.TOGGLE;
}

export interface AppMachineSetTextEvent {
  type: AppMachineEvent.SET_TEXT;
  text: string;
}

export type AppMachineEventObject = AppMachineToggleEvent | AppMachineSetTextEvent;

We can then define several actions that can be triggered in some cases, for executing any kind of side effects from the state machine.

In this case, as example, let's define an action in order to trigger again the change of the content, plus an action that can be used as callback for when the content will be changed, and execute any custom action from outside.

We can split them up into two categoriews:

  • Custom Actions: Implemented from outside for executing custom code as side effect from the machine
  • Internal Actions: Implemented inside the state machine for executing fixed side effects from the machine
export enum AppMachineAction {
  SET_TEXT = 'SET_TEXT'
}

export enum AppMachineCustomAction {
  ON_SET_TEXT = 'ON_SET_TEXT'
}

Let's then design the context of this state machine.

Being the visiblity expressed through the state, the only value that we store as context of the state machine, it is the current content that is dynamically changing through the machine.

We can define the following interface for the machine's context, and the following default initial value for it.

export interface AppMachineContext {
  text: string;
}

const initialContext: AppMachineContext = { text: 'I am a default text' };

Once all the machine behavior has been designed, we can define the current State Schema for the machine:

export interface AppMachineStateSchema extends StateSchema {
  states: {
    [key in AppMachineState]: State<AppMachineContext, AppMachineEventObject>;
  };
  context: AppMachineContext;
}

We can then define the states following the guidelines describe above:

const initialState: AppMachineState = AppMachineState.VISIBLE;

const setTextActions: string[] = [AppMachineAction.SET_TEXT, AppMachineCustomAction.ON_SET_TEXT];

const hiddenState: StateNodeConfig<
  AppMachineContext,
  AppMachineStateSchema['states'][AppMachineState.HIDDEN],
  AppMachineEventObject
> = {
  on: {
    [AppMachineEvent.TOGGLE]: { target: AppMachineState.VISIBLE },
    [AppMachineEvent.SET_TEXT]: {
      internal: true,
      actions: setTextActions,
    },
  },
};

const visibleState: StateNodeConfig<
  AppMachineContext,
  AppMachineStateSchema['states'][AppMachineState.VISIBLE],
  AppMachineEventObject
> = {
  on: {
    [AppMachineEvent.TOGGLE]: { target: AppMachineState.HIDDEN },
    [AppMachineEvent.SET_TEXT]: {
      internal: true,
      actions: setTextActions,
    },
  },
};

const states: StatesConfig<AppMachineContext, AppMachineStateSchema, AppMachineEventObject> = {
  [AppMachineState.HIDDEN]: hiddenState,
  [AppMachineState.VISIBLE]: visibleState,
};

Once all the above has been defined, we can finally create our state machine config:

export const appStateMachineConfig: MachineConfig<AppMachineContext, AppMachineStateSchema, AppMachineEventObject> = {
  initial: initialState,
  context: initialContext,
  states,
};

The last thing that we need to create, it is the definition of the static action that we want as internal side effect for our state machine, and associate it to the current options of our state machine:

const setTextAction: ActionObject<AppMachineContext, AppMachineSetTextEvent> = assign<
  AppMachineContext,
  AppMachineSetTextEvent
>((ctx: AppMachineContext, { text }: AppMachineSetTextEvent): AppMachineContext => ({ text }));

export const appStateMachineOptions: Partial<
MachineOptions<AppMachineContext, AppMachineEventObject>
> = { actions: { [AppMachineAction.SET_TEXT]: setTextAction } };

This is all that we need from the state machine side, then we can pass now to see the code we neeed to implement for the component, in order to react dynamically to this state machine.

export type SampleComponentProps = WithStateMachineProps<AppMachineContext, AppMachineEventObject>;

class SampleComponent extends PureComponent<SampleComponentProps> {
  componentDidMount(): void {
    const { extendConfig } = this.props;
    extendConfig({ actions: { [AppMachineCustomAction.ON_SET_TEXT]: this.onSetTextAction() } });
  }

  isVisible = (): boolean => {
    const { machineState } = this.props;
    return machineState.matches(AppMachineState.VISIBLE);
  };

  onToggle = (): void => {
    const { sendEvent } = this.props;
    sendEvent({ type: AppMachineEvent.TOGGLE });
  };

  onChangeText = (ev: ChangeEvent<HTMLInputElement>): void => {
    const { sendEvent } = this.props;
    sendEvent({ type: AppMachineEvent.SET_TEXT, text: ev.currentTarget.value });
  };

  onSetTextAction = (): ActionFunction<AppMachineContext, AppMachineEventObject> => (): void => { console.log('Hello world'); };

  render(): ReactNode {
    const { machineContext: { text } } = this.props;

    return (
      <div className="component-wrapper">
        <button type="button" onClick={this.onToggle}>Toggle</button>
        <input type="text" value={text} onChange={this.onChangeText} />
        <br />
        {this.isVisible() && <div>{text}</div>}
      </div>
    );
  }
}

export default withStateMachine<
AppMachineContext,
AppMachineStateSchema,
AppMachineEventObject,
WithStateMachineProps<AppMachineContext, AppMachineEventObject>
>(SampleComponent, appStateMachineConfig, appStateMachineOptions);

The code should be really familiar to all the React developers, because there is a base component which has the props injected through the HOC withStateMachine, which as you can see takes as input the base component, the machine config, and the machine options.

Through the injected prop, you can have the component that can dynamically react to the state machine changes, and triggerer changes directly to the state machines.