Skip to content

javierlopezdeancos/react-against-the-machine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

83 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

[React against the machine] [React against the machine]

An declarative state machine to react.

Activity

License GitHub issues GitHub all releases GitHub Workflow Status Coverage npm

Summary

Installation

npm i --save react-against-the-machine

Node and npm version

The project needs this nodejs and npm version restrictions:

"engines": {
  "node": ">=16.9.1 <16.8.0",
  "npm": "~7.24.0"
}

Peer dependencies

This project has these peer dependencies:

"peerDependencies": {
  "react": "^17.0.2",
  "react-dom": "^17.0.2"
}

In order to install this peer dependencies, you need an npm version up to 7.x.

This peer dependencies are installed by the npm install command.

Tasks

Buid

You could build this react state machine with:

npm run build

and take the bundle into dist folder

Test

You could run tests with:

npm run test

Test watch

Run test with watch mode:

npm run test:watch

Test coverage

Run test generating coverage report:

npm run test:coverage

Lint

lint your code with:

npm run lint

Lint fix

Fix your linted errors with:

npm run lint:fix

Format

Format your code syntax with:

npm run format

Provider

The State machine has a <MachineProvider> context API that storage all components to the Machine State and handle the state management.

useMachine

The useMachine hook is used to access the state machine in Machine context API.

import { useMachine } from 'react-against-the-machine';

const machine = useMachine();

Components

We have some pieces as react components to represent the states and transitions of the state machine.

  • Machine
  • State
  • Content
  • Transition

Components hierarchy

<MachineProvider>
  <Machine>
    <State>
      <Content>
        <ComponentExample />
      </Content>
      <Transition />
    </State>
  </Machine>
</MachineProvider>

Machine

A react component that represents the state machine wrapper.

import Machine, { MachineProvider } from 'react-against-the-machine';
import { laGuaGua as bus } from 'laguagua';

<MachineProvider>
  <Machine initial="componentA" bus={bus} logged={false}>
    <!-- here should be the state machine States -->
  </Machine>
</MachineProvider>

Machine props

Machine needs some props:

  • initial string - the initial state id of the machine.
  • bus object - the bus object of the state machine to publish/subscribe events that implement the IMachineBus interface.
  • logged boolean - the user logged status. This will be used to transition or not to some states depending on their private/public status.

We are using to our example the bus Laguagua, but you can use any other bus event that implements the IBus interface.

IMachineBus interface
export type MachineBusHandler = (message: string, data?: Object) => void;

export interface IMachineBus {
  publish: (message: string, data?: Object) => void;
  subscribe: (message: string, trigger: BusHandler) => void;
  clear: () => void;
}

State

A react component that represents a state of the state machine.

  • Any state has a unique id.
  • Any state could have transitions to other states.
  • Any state should have a content react component to render.
import Machine, { MachineProvider, State } from 'react-against-the-machine';
// import { laGuaGua as bus } from 'laguagua';

{
  /*
<MachineProvider>
  <Machine initial="componentA" bus={bus} logged={false}>
*/
}
<State id="componentA" private={false}>
  {/* here should be the state transition and the state content */}
</State>;
{
  /*
  </Machine>
</MachineProvider>
*/
}

State props

State needs some props:

  • id string - the state id to this state.
  • private boolean (default true) - if is private, the state only render the content if user is logged.
  • params string[] - params that could be read in url when machine state transition to this state and come back as a param when onEnter callback will be executed in the State lifecycle.
  • onEnter (params?: Map<string, string>) => void - is execute when the new State is mounted, come back params specified to be read in URL like a key/value map.
Example
import Machine, { MachineProvider, State } from 'react-against-the-machine';
// import { laGuaGua as bus } from 'laguagua';

const handlerEnterOnStateA = (params?: Map<string, string>): void => {
  console.log(`enter on sign up wit ${params?.get('code')} params`);
};

{
  /*
<MachineProvider>
  <Machine initial="componentA" bus={bus} logged={false}>
*/
}
<State id="A" private={false} params={['code']} onEnter={handlerEnterOnStateA}>
  {/* here should be the state transition and the state content */}
</State>;
{
  /*
  </Machine>
</MachineProvider>
*/
}

Then if user or state machine try to access to {HOST}/A?code="123" , we could get the code value when onEnter state callback was executed.

Transition

A react component that represents a transition to other state

  • Transition should be placed inside the <State /> component that wants to go to another state.
import { Transition } from 'react-against-the-machine';

<Transition event="go::componentA" state="componentA" />;

Transition props

Transition needs some props:

  • event string - the event to trigger this transition.
  • state string - the state id to go to.

Content

A react component that render a react component that be wrapper by when machine is in this state.

Usage

Basic example

stateDiagram-v2
  componentA --> componentB: go-to-componentB
  componentB--> componentA: go-to-componentA
Loading
import React from 'react';

// import the react against the machine pieces
import Machine, { MachineProvider, State, Transition, Content } from 'react-against-the-machine';
// import any bus that implements the IBus interface
import { laGuaGua as bus } from 'laguagua';

import ComponentA from './componentA';
import ComponentB from './componentB';

const App = () => {
  const onTransitionToComponentB = (): void => {
    console.log('Hey we are in component B');
  };

  return (
    <MachineProvider>
      <Machine initial="componentA" bus={bus} logger={true}>
        <State id="componentA" private={false}>
          <Content>
            <ComponentA />
          </Content>
          <Transition event="go:to:componentB" state="componentB" onEnter={onTransitionToComponentB} />
        </State>

        <State id="componentB" private={false}>
          <Content>
            <ComponentB />
          </Content>
          <Transition event="go:to:componentA" state="componentA" />
        </State>
      </Machine>
    </MachineProvider>
  );
};

export default App;

Real example

[example]

You could build and run the real example that we have here:

cd src/example-ratm
npm i
npm start

Read params from url

Put follow URL in your local browser to read code param in url

http://localhost:3000/State3?code="123"