Higher Order Component to introduce Finite State Machine to React Components
The logic is based on this article. If you want to fully understand what's happening in this package and why you should use it, definitely read it!
npm install fsm-hoc --save
This module provides a function to wrap your components in a finite state machine.
This gives your component to additional props: status
and transition
.
The status is the current status of the component.
Transition is a function that takes as first argument this
, and as second an object with
type
key that contains the Action
.
The Props interface of your component you want to wrap
The State interface of your component you want to wrap
An enum that lists the statuses your component can be in
An enum that lists the actions that can be triggered to change a status
This is an object that describes which actions can be triggered on which statuses, and the status they go into.
const statusMachine = {
[Status.Idle]: {
[Action.Submit]: Status.Loading,
},
[Status.Loading]: {
[Action.Error]: Status.Error,
[Action.Success]: Status.Success,
},
[Status.Error]: {
[Action.Submit]: Status.Loading,
},
};
Commands are side-effects that are triggered when a component changes to a new status.
const commands = {
[Status.Error]: (component: React.Component<Props, State>) => {
// Side effects
component.setState({ errorMsg: 'Wrong parameters.' });
},
};
The status to start in.
import { fsm, InjectedProps } from 'fsm-hoc';
enum Status {
Idle,
Loading,
Error,
Success
}
enum Action {
Submit, Error, Success
}
const statusMachine = {
[Status.Idle]: {
[Action.Submit]: Status.Loading,
},
[Status.Loading]: {
[Action.Error]: Status.Error,
[Action.Success]: Status.Success,
},
[Status.Error]: {
[Action.Submit]: Status.Loading,
},
};
const commands = {
[Status.Error]: (component: React.Component<Props, State>) => {
// Side effects
component.setState({ errorMsg: 'Wrong parameters.' });
},
};
class Form extends React.Component<Props & InjectedProps<Status, Action>, State> {
...
onSubmit() {
this.props.transition(this, { type: Action.Submit });
}
render() {
return (
...
<input
type="button"
disabled={this.props.status !== Status.Loading}
onClick={this.onSubmit}
>
Submit
</input>
...
);
}
...
}
export default fsm<Props, State, Status, Action>(statusMachine, commands, Status.Idle)(Form);