Close Menu
Soshace Digital Blog

    Subscribe to Updates

    Get The Latest News, Updates, And Amazing Offers

    What's Hot
    Programming

    3. Уроки Express.js. Шаблонизация с EJS: Layout, Block, Partials

    Programming

    6. Уроки Node.js. Util и Наследование

    Beginners

    WordPress for Non-Programmers: Introduction to the Web Development World

    Important Pages:
    • Home
    • About
    • Services
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    Facebook X (Twitter) Instagram LinkedIn YouTube
    Today's Picks:
    • Scaling Success: Monitoring Indexation of Programmatic SEO Content
    • Leveraging Influencers: Key Drivers in New Product Launches
    • How Privacy-First Marketing Will Transform the Industry Landscape
    • The Impact of Social Proof on Thought Leadership Marketing
    • Balancing Value-Driven Content and Promotional Messaging Strategies
    • Top Influencer Marketing Platforms to Explore in 2025
    • Emerging Trends in Marketing Automation and AI Tools for 2023
    • Strategies to Mitigate Duplicate Content in Programmatic SEO
    Wednesday, September 10
    Facebook X (Twitter) Instagram LinkedIn YouTube
    Soshace Digital Blog
    • Home
    • About
    • Services
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    Services
    • SaaS & Tech

      Maximizing Efficiency: How SaaS Lowers IT Infrastructure Costs

      August 27, 2025

      Navigating Tomorrow: Innovations Shaping the Future of SaaS

      August 27, 2025

      Maximizing Impact: Strategies for SaaS & Technology Marketing

      August 27, 2025
    • AI & Automation

      Enhancing Customer Feedback Analysis Through AI Innovations

      August 27, 2025

      Navigating the Impact of AI on SEO and Search Rankings

      August 27, 2025

      5 Automation Hacks Every Home Service Business Needs to Know

      May 3, 2025
    • Finance & Fintech

      Critical Missteps in Finance Marketing: What to Avoid

      August 27, 2025

      Analyzing Future Fintech Marketing Trends: Insights Ahead

      August 27, 2025

      Navigating the Complex Landscape of Finance and Fintech Marketing

      August 27, 2025
    • Legal & Compliance

      Exploring Thought Leadership’s Impact on Legal Marketing

      August 27, 2025

      Maximizing LinkedIn: Strategies for Legal and Compliance Marketing

      August 27, 2025

      Why Transparency Matters in Legal Advertising Practices

      August 27, 2025
    • Medical Marketing

      Enhancing Online Reputation Management in Hospitals: A Guide

      August 27, 2025

      Analyzing Emerging Trends in Health and Medical Marketing

      August 27, 2025

      Exploring Innovative Content Ideas for Wellness Blogs and Clinics

      August 27, 2025
    • E-commerce & Retail

      Strategic Seasonal Campaign Concepts for Online and Retail Markets

      August 27, 2025

      Emerging Trends in E-commerce and Retail Marketing Strategies

      August 27, 2025

      Maximizing Revenue: The Advantages of Affiliate Marketing for E-Commerce

      August 27, 2025
    • Influencer & Community

      Leveraging Influencers: Key Drivers in New Product Launches

      August 27, 2025

      Top Influencer Marketing Platforms to Explore in 2025

      August 27, 2025

      Key Strategies for Successful Influencer Partnership Negotiations

      August 27, 2025
    • Content & Leadership

      The Impact of Social Proof on Thought Leadership Marketing

      August 27, 2025

      Balancing Value-Driven Content and Promotional Messaging Strategies

      August 27, 2025

      Analyzing Storytelling’s Impact on Content Marketing Effectiveness

      August 27, 2025
    • SEO & Analytics

      Scaling Success: Monitoring Indexation of Programmatic SEO Content

      August 27, 2025

      Strategies to Mitigate Duplicate Content in Programmatic SEO

      August 27, 2025

      Effective Data Visualization Techniques for SEO Reporting

      August 27, 2025
    • Marketing Trends

      How Privacy-First Marketing Will Transform the Industry Landscape

      August 27, 2025

      Emerging Trends in Marketing Automation and AI Tools for 2023

      August 27, 2025

      Maximizing ROI: Key Trends in Paid Social Advertising

      August 27, 2025
    Soshace Digital Blog
    Blog / JavaScript / React / An Introduction to Finite State Machines: Simplifying React State Management with State Machines
    JavaScript

    An Introduction to Finite State Machines: Simplifying React State Management with State Machines

    bradstarartBy bradstarartFebruary 2, 2021No Comments11 Mins Read
    Facebook Twitter Pinterest Telegram LinkedIn Tumblr Email Reddit
    An Introduction to Finite State Machines: Simplifying React State Management with State Machines
    An Introduction to Finite State Machines: Simplifying React State Management with State Machines
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link
    An Introduction to Finite State Machines: Simplifying React State Management with State Machines
    An Introduction to Finite State Machines: Simplifying React State Management with State Machines

    As their name suggests, a finite state machine is an abstract machine that can only exist in one of any number of (finite) states at once. Such a machine accepts any number of inputs, and, depending on their sequence, changes its internal state and produces an output.

    A common example of a state machine that you’ve likely interacted within the real world is an elevator. It can either be going up or down (never both at once), and will stop in the sequence in which floors were input. Another example is a microwave. If the door is open and you press start, nothing happens. If the microwave is running and someone suddenly opens the door, it switches state from ‘running’ to ‘stopped’. Such a change in state is referred to as a transition.

    But how does that relate to code? Let’s start with an example of how not to manage React state before we move on to demonstrate how useful state machines can be for simplifying your code.

    How to poorly manage React state

    One of the most common use-cases on the web that can benefit from the use of state machines is forms. Typically, forms exist in three different states: ‘submitting’, ‘error’, and ‘success’.

    Let’s use the following simple form, which allows a user to input the number of car parts they want to order from their favorite automotive company.

    import React, {useState} from "react";
    import {Button, Input} from "bloomer";
     
    function App() {
     
      const [numberOfCarParts, setNumberOfCarParts] = useState(0);
      const [carPartNo, setCarPartNo] = useState('');
      const [isSuccess, setIsSuccess] = useState(false);
      const [isError, setIsError] = useState(false);
      const [isLoading, setIsLoading] = useState(false);
     
      async function completeOrder(order){
        setIsLoading(true);
        try {
          await sendRequestToServer(order);
          setIsSuccess(true);
        }catch (e){
          setIsError(true)
        }
        setIsLoading(false);
      }
     
      const flipCoin = () => Math.random() < 0.5;
     
      async function sendRequestToServer() {
        return new Promise(((resolve, reject) => {
          setTimeout(()=> {
            if (flipCoin()) {
              reject("Oops. An error occurred");
              return;
            }
     
            resolve("Your order is on it's way!")
          }, 2000)
        }))
      }
     
        return (
            <div>
                <div>
                    {
                        // only show this message if the request was completed successfully
                        isSuccess ? (
                            <p>Great! Your new car parts are on the way</p>
                        ) : isError ? (
                            <p>Sorry! an error occurred!</p>
                        ) : <span/>
                    }
                </div>
                <form
                    onSubmit={() => completeOrder({
                        numberOfCarParts,
                        nameOfCarPart: carPartNo
                    })}>
                    <label>
                        Car part number
                    </label>
                    <Input
                        type={'text'}
                        placeholder={'Car part number'}
                        onChange={(e)=> {
                            setCarPartNo(e.target.value)
                        }}
                    />
                    <label>Number of cart parts</label>
                    <Input
                        onChange={(e) => {
                            setNumberOfCarParts(Number(e.target.value));
                        }}
                        placeholder={'100'}
                        min={1}
                        type={'number'}/>
                    <Button
                        isLoading={isLoading}
                        isColor={'primary'}
                        type={'submit'}>
                        Complete order
                    </Button>
                </form>
            </div>
        );
    }
     
    export default App;

    As far as code goes, that’s not too bad.

    We handle each of the three form states using booleans. Since we’re using Bulma for this project, it will handle hiding the button text and showing a spinner when isLoading is true. When our fake request is completed, it results in either an error or a success result, which is stored in state. In fact, we might be tempted to pat ourselves for a good job done, commit the code and create a pull request to merge our code to master. But let’s not get ahead of ourselves. There’s a good chance any experienced developer will smell the bad code we’ve written here from a mile away.

    In computer science, there’s a phenomenon referred to as combinatorial explosion, but since we’re using booleans to handle our state, our problem is referred to as boolean explosion. The boolean explosion is the rapid increase of the complexity of a problem due to the addition of new boolean parameters/states. It happens at the rate of 2^n, where n is the number of states.

    The maths works itself out like so

    • 1 boolean => 2^1 = 2 states
    • 2 booleans => 2^2 = 4 states
    • 3 booleans => 2^3 = 8 states
    • 4 booleans => 2^4 = 16 states

    But the code above has just 3 states, not 8. This is owed to the fact that there are plenty of impossible states in our code that would be a pain to maintain or refactor later on. For instance, the ‘error’ and ‘success’ state will never exist together, so there’s definitely room to improve our code.

    Read More:  How to Create a Personal Blogging Website: Front-End (Angular) #2

    Using state machines to simplify React state

    As mentioned above, finite state machines exist in a known number of states, and can only transition in a specific order. For instance, it doesn’t make sense for a form to move from an ‘error’ state to a ‘success’ state or vice versa. The order has to be ‘idle’ -> ‘submitting’ then either ‘success’ or ‘error’.

    Therefore, we need to define the logic that will allow us to:

    1. Get rid of impossible states
    2. Define strict transitions in which our state is allowed to move.

    To achieve this, we will start by enumerating (manually defining) the list of all possible states and their transitions:

    const states = {
       idle: 'idle',
       isLoading: 'loading',
       isError: 'error',
       isSuccess: 'success'
    }
     
    const transitions = {
       [states.idle]: {
           SUBMIT_FORM: states.isLoading
       },
       [states.isLoading]: {
           SUBMIT_FORM_SUCCESS: states.isSuccess,
           SUBMIT_FORM_ERROR: states.isError
       },
       [states.isError]: {
           SUBMIT_FORM: states.isLoading
       },
       [states.isSuccess]: {
           RESET_FORM: states.idle
       }
    }
    

    We also need a function that will change the current state to whatever relevant state is next when we pass it an action.

    function transition(currentState, action){
       const nextState = transitions[currentState][action];
       return nextState || currentState;
    }
     
    function updateFormState(action) {
       setCurrentState(currentState => transition(currentState, action));
    }
    

    The transition function receives the current state together with the action that we want to be performed. For instance, if we pass the parameters ‘idle’ and ‘submit’, the next state will always be ‘loading’. This will then be the new currentState. Additionally, if anyone passes an invalid action, we simply return the current state.

    And, with that, we can update our code:

    import React, {useState} from "react";
    import {Button, Input} from "bloomer";
     
    function App() {
        const states = {
            idle: 'idle',
            isLoading: 'loading',
            isError: 'error',
            isSuccess: 'success'
        }
        const [numberOfCarParts, setNumberOfCarParts] = useState(null);
        const [carPartNo, setCarPartNo] = useState(null);
        const [currentState, setCurrentState] = useState(states.idle);
     
        const transitions = {
            [states.idle]: {
                SUBMIT_FORM: states.isLoading
            },
            [states.isLoading]: {
                SUBMIT_FORM_SUCCESS: states.isSuccess,
                SUBMIT_FORM_ERROR: states.isError
            },
            [states.isError]: {
                SUBMIT_FORM: states.isLoading
            },
            [states.isSuccess]: {
                RESET_FORM: states.idle
            }
        }
     
        function transition(currentState, action){
            const nextState = transitions[currentState][action];
            return nextState || currentState;
        }
     
        function updateFormState(action) {
            setCurrentState(currentState => transition(currentState, action));
        }
     
        async function completeOrder(order){
            updateFormState('SUBMIT_FORM');
            try {
                await sendRequestToServer(order);
                updateFormState('SUBMIT_FORM_SUCCESS');
            }catch (e){
                updateFormState('SUBMIT_FORM_ERROR');
            }
        }
     
        const flipCoin = () => Math.random() < 0.5;
     
        async function sendRequestToServer() {
            return new Promise(((resolve, reject) => {
                setTimeout(()=> {
                    if (flipCoin()) {
                        reject("Oops. An error occurred");
                        return;
                    }
     
                    resolve("Your order is on it's way!")
                }, 2000)
            }))
        }
     
        return (
            <div>
                <div>
                    {
                        currentState === states.isSuccess ? (
                            <p>Great! Your new car parts are on the way</p>
                        ) : currentState === states.isError ? (
                            <p>Sorry! an error occurred!</p>
                        ) : <span/>
                    }
                </div>
                <form
                    onSubmit={() => completeOrder({
                        numberOfCarParts,
                        nameOfCarPart: carPartNo
                    })}>
                    <label>
                        Car part number
                    </label>
                    <Input
                        type={'text'}
                        placeholder={'Car part number'}
                        onChange={(e) => {
                            setCarPartNo(e.target.value)
                        }}
                    />
                    <label>Number of cart parts</label>
                    <Input
                        onChange={(e) => {
                            setNumberOfCarParts(Number(e.target.value));
                        }}
                        placeholder={'100'}
                        min={1}
                        type={'number'}/>
                    <Button
                        isLoading={currentState === states.isLoading}
                        isColor={'primary'}
                        type={'submit'}>
                        Complete order
                    </Button>
                </form>
            </div>
        );
    }
     
    export default App;

    So far, we have managed to get rid of a ton of complexity, but we’re not there yet. We have reduced the number of states we track from 8 to just one. But our solution still isn’t ideal.

    Say we wanted to add a new state to our form. We would first have to add it to our states object then add it to the transitions object together with its corresponding transitions. This same kind of syncing would also happen if we wanted to rename one of the state objects. To rename isLoading to isSubmitting, for example, we would have to rename it in two places, which, again, makes refactoring a pain in the butt.

    It’s even worse if you consider the implication of implementing a different component using state machines. You would have to create separate transitions for that component as well, then keep it in sync with its own state enum, which isn’t ideal.

    Instead, let’s move on to the final part of our journey with state machines.

    Read More:  Consequat Mauris Nunc Congue Nisivitae Tellus Consectetur

    Using XState to manage finite state machines

    XState is a library that encapsulates all the logic for creating, interpreting, and executing finite state machines. It can also generate state machine diagrams to help you visualize how your program is going to run. To install it, run:

    npm install xstate @xstate/react

    First, let’s define all the states the form is going to exist in:

    const formMachine = new Machine({
       initial: 'idle',
       states: {
           idle: {},
           isLoading: {},
           isError: {},
           isSuccess: {},
       }
    })
    

    Next, we define all the transitions we want our form to exist in

    const formMachine = new Machine({
       initial: 'idle',
       states: {
           idle: {
               on: {
                   SUBMIT_FORM: 'isLoading'
               }
           },
           isLoading: {
               on: {
                   SUBMIT_FORM_SUCCESS: 'isSuccess',
                   SUBMIT_FORM_ERROR: 'isError'
               }
           },
           isError: {
               on: {
                   SUBMIT_FORM: 'isLoading'
               }
           },
           isSuccess: {
               on: {
                   RESET_FORM: 'idle'
               }
           },
       }
    })
    

    This looks a lot like the function we wrote before and reads just the same. From the idle state, we transition to the isLoading state when a SUBMIT_FORM action is called. From the isLoading state, we transition to either isSuccess or isError depending on the action supplied to the machine, and so on.

    All that’s left is to hook up the machine to XState.

    const [currentState, send] = useMachine(formMachine);

    currentState is an object provided by XState that gives us access to a .matches, which allows us to check for the current internal state of the machine. send is a function that accepts the action that we want to be executed. It’s used like so:

    const  updateFormState = (action)=> send(action)
    
    async function completeOrder(order){
       updateFormState('SUBMIT_FORM');
       try {
           await sendRequestToServer(order);
           updateFormState('SUBMIT_FORM_SUCCESS');
       }catch (e){
           updateFormState('SUBMIT_FORM_ERROR');
       }
    }
    

    With that, here is the final result:

    import React, {useState} from "react";
    import {Button} from "bloomer";
    import {Machine} from "xstate";
    import {useMachine} from "@xstate/react";
    
    const formMachine = new Machine({
       initial: 'idle',
       states: {
           idle: {
               on: {
                   SUBMIT_FORM: 'isLoading'
               }
           },
           isLoading: {
               on: {
                   SUBMIT_FORM_SUCCESS: 'isSuccess',
                   SUBMIT_FORM_ERROR: 'isError'
               }
           },
           isError: {
               on: {
                   SUBMIT_FORM: 'isLoading'
               }
           },
           isSuccess: {
               on: {
                   RESET_FORM: 'idle'
               }
           },
       }
    })
    
    function App() {
    
       const [numberOfCarParts, setNumberOfCarParts] = useState(null);
       const [currentState, send] = useMachine(formMachine);
    
       const  updateFormState = (action)=> send(action)
    
       async function completeOrder(order){
           updateFormState('SUBMIT_FORM');
           try {
               await sendRequestToServer(order);
               updateFormState('SUBMIT_FORM_SUCCESS');
           }catch (e){
               updateFormState('SUBMIT_FORM_ERROR');
           }
       }
    
       const flipCoin = () => Math.random() < 0.5;
    
       async function sendRequestToServer() {
           return new Promise(((resolve, reject) => {
               setTimeout(()=> {
                   if (flipCoin()) {
                       reject("Oops. An error occurred");
                       return;
                   }
    
                   resolve("Your order is on it's way!")
               }, 2000)
           }))
       }
    
       return (
           <div>
               <div>
                   {
                       // only show this message if the request was completed successfully
                       currentState.matches('isSuccess') ? (
                           <p>Great! Your new car parts are on the way</p>
                       ): currentState.matches('isError') ? (
                           <p>Sorry! an error occurred!</p>
                       ): <span/>
                   }
               </div>
               <form onSubmit={() => completeOrder({
                   numberOfCarParts
               })}>
                   <label>Number of cart parts</label>
                   <input
                       onChange={(e) => {
                           setNumberOfCarParts(Number(e.target.value));
                       }}
                       placeholder={100}
                       min={1}
                       type={'number'}/>
                   {
                       // don't show the submit button when
                       // the form is loading
                       <Button
                           isColor={'primary'}
                           isLoading={currentState.matches('isLoading')}
                       >
    
                       </Button>
                   }
               </form>
           </div>
       );
    }
    
    export default App;

    In this article, we’ve covered a simple use-case for managing React state with state machines.

    More complicated applications might leverage some of the XState library’s more advanced functionality. You can have machines communicate with each other and invoke callbacks or promises in response to different events being completed. Since this was meant to be an introductory article, it hasn’t covered those use cases, but the XState documentation does a good job of covering a lot of different applications.

    Conclusion

    Managing state in React isn’t always straightforward, especially when using react hooks. State machines help to take a lot of complexity out of the equation by defining all possible states a component can be in, eliminating impossible states and preventing boolean explosions in the process.

    They can be used to define behaviour for pretty much every kind of component – modals, forms, menus, buttons… e.t.c. Using XState, you can also visualize your component in diagram form. This allows you to map out your whole component before you even write a line of code.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    bradstarart

      Related Posts

      Streamlining Resource Allocation for Enhanced Project Success

      December 18, 2024

      Conducting Effective Post-Project Evaluations: A Guide

      December 16, 2024

      Strategies for Keeping Projects on Track and Meeting Deadlines

      December 10, 2024
      Leave A Reply Cancel Reply

      You must be logged in to post a comment.

      Stay In Touch
      • Facebook
      • Twitter
      • Pinterest
      • Instagram
      • YouTube
      • Vimeo
      Don't Miss
      Beginners August 21, 2019

      Design Patterns Overview: Helping You Write Better Software

      In this article, we will explore the most popular software design patterns like singleton, decorator, observer, and factory. We’ll also discuss why it’s important to know them well — and then we’ll provide you with some great learning resources.

      Advanced Node.Js: A Hands on Guide to Event Loop, Child Process and Worker Threads in Node.Js

      January 24, 2020

      Why Working Remotely Is a Double-Edged Sword

      August 9, 2019

      Tech Blogging for Web Developers in 2019: Why? Which Platform? How to Start Your Own Tech Blog?

      July 16, 2019

      Categories

      • AI & Automation
      • Angular
      • ASP.NET
      • AWS
      • B2B Leads
      • Beginners
      • Blogs
      • Business Growth
      • Case Studies
      • Comics
      • Consultation
      • Content & Leadership
      • CSS
      • Development
      • Django
      • E-commerce & Retail
      • Entrepreneurs
      • Entrepreneurship
      • Events
      • Express.js
      • Facebook Ads
      • Finance & Fintech
      • Flask
      • Flutter
      • Franchising
      • Funnel Strategy
      • Git
      • GraphQL
      • Home Services Marketing
      • Influencer & Community
      • Interview
      • Java
      • Java Spring
      • JavaScript
      • Job
      • Laravel
      • Lead Generation
      • Legal & Compliance
      • LinkedIn
      • Machine Learning
      • Marketing Trends
      • Medical Marketing
      • MSP Lead Generation
      • MSP Marketing
      • NestJS
      • Next.js
      • Node.js
      • Node.js Lessons
      • Paid Advertising
      • PHP
      • Podcasts
      • POS Tutorial
      • Programming
      • Programming
      • Python
      • React
      • React Lessons
      • React Native
      • React Native Lessons
      • Recruitment
      • Remote Job
      • SaaS & Tech
      • SEO & Analytics
      • Soshace
      • Startups
      • Swarm Intelligence
      • Tips
      • Trends
      • Vue
      • Wiki
      • WordPress
      Top Posts

      Training DALL·E on Custom Datasets: A Practical Guide

      Programming January 19, 2024

      Deploying Your NodeJS Code to a Server Every Time You Push with Github Actions

      Node.js June 29, 2020

      Effective Networking Strategies to Generate B2B Leads

      B2B Leads December 18, 2024

      Development With LAMP Stack Illustrated Address Book Project

      Beginners July 22, 2020

      Subscribe to Updates

      Get The Latest News, Updates, And Amazing Offers

      About Us
      About Us

      Soshace Digital delivers comprehensive web design and development solutions tailored to your business objectives. Your website will be meticulously designed and developed by our team of seasoned professionals, who combine creative expertise with technical excellence to transform your vision into a high-impact, user-centric digital experience that elevates your brand and drives measurable results.

      7901 4th St N, Suite 28690
      Saint Petersburg, FL 33702-4305
      Phone: 1(877)SOSHACE

      Facebook X (Twitter) Instagram Pinterest YouTube LinkedIn
      Our Picks
      B2B Leads

      Leveraging Case Studies and Testimonials for B2B Leads

      Development

      Enhancing Code Quality: Best Practices for Software Development

      JavaScript

      React Lesson 5: React Devtools and Reusable Open-Source Components

      Most Popular

      19. Уроки Node.js. Безопасный Путь к Файлу в fs и path.

      Programming

      How Facebook Ads Can Skyrocket Your Home Services Bookings

      Facebook Ads

      22. Чат через long-polling, чтение POST. Pt.1.

      Programming
      © 2025 Soshace Digital.
      • Home
      • About
      • Services
      • Contact Us
      • Privacy Policy
      • Terms & Conditions

      Type above and press Enter to search. Press Esc to cancel.