Home > database >  React JS multi-inputs with asynchronous data validation and race condition
React JS multi-inputs with asynchronous data validation and race condition

Time:03-01

I'm new to React and I want to implement a form where the user is given N inputs and is asked to copy/paste some contents in it (he can also click on a or - button to add or remove some inputs but let's keep it simple for now).

The idea is that, for each input, the user will copy/paste some data and, on each input an onChange listener is attached so that each time an input is changed, it will trigger a call to an API or backend service to validate the data. Once the data is validated, it will return whether or not the user was right (by changing the background color of the input for example). Here is a picture:

enter image description here

The problem is that, let say the user copies/pastes gorilla in the first input and the async call takes 10s, but then 2s after it copied/pasted gorilla in the first input, he copies/pastes spaceship in the second input (and this async call takes 4s to return let's say). What happens with my current version is that, since the 2nd call finished before the first one, isValid is updated for the second input while it is not for the first input.

This is due to the fact that I'm using useEffect and the clean function. I have also tried to use useState instead but each time I ran into a race condition...

Notes:

  • If the user entered gorilla in the first input and 1s after he modifies the same input and enters giraffe. If the first async call did not finish yet, I prefer to kill it and only retrieve the validation for the latest call made for that same input (this is why I used a clean function in my useEffect)
  • Even if the problem of stopping the validation of other inputs is solved, I think there will always be a race condition as in my useEffect I am modifying my useState associated to each of my input so I'm not sure how to go about it.

Here is tiny sample to replicate my current behavior:

import React, { useEffect, useState } from "react";

export default function MyComponent(props) {
  let delay = 10000; // ms
  const [data, setData] = useState(
    Array(5).fill({
      isValid: 0,
      url: null
    })
  );
  const [inputIdx, setInputIdx] = useState(-1);
  const updateData = (index, datum) => {
    const updatedData = data;
    updatedData[index] = datum;
    setData([...updatedData]);
    setInputIdx(index);
    delay /= 2;
  };
  useEffect(() => {
    let canceled = true;

    new Promise((resolve) => {
      setTimeout(() => resolve("done"), delay);
    }).then((e) => {
      if (canceled) {
        const updatedData = data;
        updatedData[inputIdx].isValid = true ^ updatedData[inputIdx].isValid;
        setData([...updatedData]);
      }
    });
    return () => {
      canceled = false;
    };
  }, [inputIdx]);
  const processData = data.map((datum, index) => {
    return (
      <input
        type="text"
        onChange={e =>
          updateData(index, {
            url: e.target.value === "" ? null : e.target.value,
            isValid: data[index].isValid,
          })
        }
        style={{display:"block"}}
      />
    );
  });
  return (
    <div>
      <div>{processData}</div>
      <div>{JSON.stringify({data})}</div>
    </div>
  );
}

Here is a enter image description here

Edit react-js-multi-inputs-with-asynchronous-data-validation-and-race-condition

  • Related