One interesting thing about the various elm-like React reducer patterns/libraries is that you essentially end up writing a state machine anyway. ReasonML's react bindings have a reducer component available out of the box, making it easy to write a 'pure' state machine and then wire it up to the UI via a React shell.
This allows you to do interesting things like generating instances of your UI state that match particular logical constraints (https://medium.com/imandra/constraint-solving-your-uis-8933f...) or formally proving properties about the state machine (https://medium.com/imandra/verifying-reasonreact-component-l...), but working directly with the code of the state machine itself rather than on a separate spec.
Disclaimer: I work for AI developing Imandra working on these tools.