Creating flow using useState

nmiller15

Nolan Miller

Posted on July 20, 2024

Creating flow using useState

Today it seemed that I’m not moving as quickly as I have the past few days.

I started work today on the card that most of the user interaction happens with. And since this is the part I care the most about, this is also taking the most time! I spent a good couple of hours fighting with the form, trying to implement an animation for the form labels. I did eventually get it, but after working on it for so long, I feel like I need a bit of a break.

Either way, I found a solution for the bigger picture flow of how the card works.

Home screen UI

Why Do I Need Flow?

When I hit the “Start a new roast” button, there multiple screens that need to be passed through in one card. A form to get information about the coffee and beans being used and some of the parameters of the roast, the actual timer with the breakpoints for different steps, another form that takes in some of the after-roast details and a summary view that shows all of the details of the roast that have been calculated in the end.

I needed an elegant way of getting from one screen to the next, all within one component.

Implementing useState to Create Flow

I have three different states that control the rendering and the appearance of my Roaster component.

Within my Home component, I initialized a state that tracks wether or not the roaster is active, and then I conditionally render out my Roaster component if it's active.



// Home.js
// .. imports
import { useState } from 'react'

function Home() {
  const [roastActive, setRoastActive] = useState(false);
  const newRoast = new Roast(Date.now());

  const handleNewRoast = () => setRoastActive(true);
  const closeRoaster = () => setRoastActive(false);

  return !roastActive ?
    (
      <> {/* My Home component */}</>
    )
    :
    (
      <Roaster roast={newRoast} close={closeRoaster} />
    )
}


Enter fullscreen mode Exit fullscreen mode

When the Roaster component is active, I’ve thrown the closeRoaster function down to it as a prop so that I can escape the window without having to navigate away from this page.

What’s going on in Roaster then?

In the Roaster component, I declared two more state variables that keep track of where the user is in the roasting flow. One of them tracks the view of the card and the other tracks the step within the actual roast.

Take a look:



// Roaster.js
// ... imports
import { useState } from 'react'

function Roaster() {
  const [progress, setProgress] = useState("start-roast-form")
  // start-roast-form > roast-active > finish-roast-form > roast-complete
  const [roastStep, setRoastStep] = useState(1);
  // steps are 1-5


Enter fullscreen mode Exit fullscreen mode

As you can see from my comments, I’m using string values to move through the progress state. This is to make it easier to work with as I jump up and down this file.

The roastStep state is just integers, because they are actually labeled in the UI with numbers.

Then I created two little helper functions that move users through the stages an eventually closes the card with useEffect once it's been set to 'inactive’ .



// Move through the different roaster states
  const nextProgress = () => {
    setProgress(progress === 'start-roast-form'
      ? 'roast-active'
      : progress === 'roast-active'
      ? 'finish-roast-form'
      : progress === 'finish-roast-form'
      ? 'roast-complete'
      : progress === 'roast-complete'
      ? 'inactive'
      : 'inactive' 
    )
  }

  // Control steps during the roasting
  const nextStep = () => {
    if (roastStep < 5) {
      setRoastStep(roastStep + 1);
    } else {
      nextProgress();
    }
  }

  // Trigger rerender of the Home Page component
  useEffect(() => {
    if (progress === 'inactive') {
      close();
    }
  })


Enter fullscreen mode Exit fullscreen mode

Then all that’s left is to do is to conditionally render out the pieces of the UI that I want to for the different states:



return progress === 'start-roast-form' ?
    (
      <></>
    ) 
    : progress === 'roast-active' ?
    (
      <></>
    )
    : progress === 'finish-roast-form' ?
    (
      <></>
    )
    : progress === 'roast-complete' ?
    (
      <></>
    )
    : <></>
}


Enter fullscreen mode Exit fullscreen mode

Obviously, replacing the fragments here with the components that make up each of the views.

What Do We Get?

Flow of the Roaster Component

Check out the gif above for the general loop of the UI! I don’t have all of the UI written in yet, but you get a general idea of the flow.

The form opens, then you get to the roast page, where you progress through 5 steps (you can’t see me tapping the “Record step” button 5 times), and then we cycle through the next two pages before closing the card and re-rendering our home page!

One of these days, I’ll write those active state styling into the button…

Thanks for reading!


Check Out the Repo

If you want to keep up with the changes, fork and run locally, or even suggest code changes, here’s a link to the GitHub repo!

https://github.com/nmiller15/roast

💖 💪 🙅 🚩
nmiller15
Nolan Miller

Posted on July 20, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Creating flow using useState
react Creating flow using useState

July 20, 2024