Blog

React Component – Incrementor with Submit Delay using Hooks

, John Comber

Introduction

I recently had a requirement to develop a React component that would provide the functionality to increment / decrement a value n times but only submit a single request to the back end with the final value.

I implemented this using React hooks and setTimeout.

Code

// JavaScript / JSX

import React, { useState, useReducer, useEffect, useRef } from "react"

import * as api from "../api/values"

const requiredValueActions = {
  INCREASE: "INCREASE",
  DECREASE: "DECREASE",
}

function requiredValueReducer(state, action) {
  switch (action.type) {
    case requiredValueActions.INCREASE:
      return { requiredValue: state.requiredValue + 1 }
    case requiredValueActions.DECREASE:
      return { requiredValue: state.requiredValue - 1 }
    default:
      throw new Error() // TODO Define error
  }
}

function ValueIncrementor(props) {
  const [state, dispatchRequiredValueChange] = useReducer(
    requiredValueReducer,
    { requiredValue: props.requiredValue }
  )

  // Track the id returned from the setTimeout function so that it can be cancelled
  // if the user changes the value again within the timeout delay period.
  const [timeoutId, setTimeoutId] = useState(null)

  // Track whether this is the first update.  Only run useEffect code if the user
  // has changed the state.requiredValue (as opposed to first run).
  const firstUpdate = useRef(true)

  // requiredValue
  useEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false
    } else {
      setTimeoutId(
        setTimeout(
          () => api.addRequiredValue(props.id, state.requiredValue),
          5000
        )
      ) // TODO Move timeout ms to configuration
    }
  }, [state.requiredValue])

  const handleChange = (requiredValueAction) => {
    // If an existing timeout is running then cancel this.
    if (timeoutId) {
      clearTimeout(timeoutId)
    }
    dispatchRequiredValueChange({ type: requiredValueAction })
  }

  return (
    <div>
      <h1>
        {props.name} (Id: {props.id})
      </h1>
      <h2>Actual Value</h2>
      <p>{props.actualValue}</p>
      <h2>Required Value</h2>
      <button
        type="button"
        className="btn btn-lg"
        onClick={() => handleChange(requiredValueActions.INCREASE)}
      >
        + 1
      </button>
      <p>{state.requiredValue}</p>
      <button
        type="button"
        className="btn btn-lg"
        onClick={() => handleChange(requiredValueActions.DECREASE)}
      >
        - 1
      </button>
    </div>
  )
}

export default ValueIncrementor

GitHub

The code is also available on GitHub here:

https://github.com/iskarconsulting/react/tree/main/increment-delay-submit

Licence

MIT