Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - Any way to cancel (or ignore) async action on 'OnChange' handler?

I have a code here https://codesandbox.io/embed/cranky-glade-82leu

In here, I have a 'verifyUserNameAvailable' function under 'api/' folder. In this function, I added 10sec delay to test extreme case when our API server is bad.

When user enters a valid email address (valid email format like [email protected]), I see it's email address in helperText line 10 sec later. (This is because I added 10 sec delay in 'verifyUserNameAvailable'.)

When user enters '[email protected]', '[email protected]' and '[email protected]' 3 emails one by one without waiting for 10 seconds, I see that all three emails are displayed in sequential order on helperText line. (I guess this is expected given what I have in the code now.)

My question here-> I want the helperText line to show the most latest one only. When user enters above 3 email addresses and '[email protected]' is the last one on the textField, I want to just show the '[email protected]' on helperText line not the others.

Is there anyway to check what's actually on textfield and discard or abort all irrelevant async requests?

like image 518
hinewwiner Avatar asked Dec 20 '25 23:12

hinewwiner


1 Answers

There's a way to do this without cancelling the verifyUserNameAvailable promise: Keep track of the number of requests in flight, and ignore any responses other than the latest one.

Here's what that could look like in your example, this uses useRef to keep track of the latest request value (see the React docs for useRef to see how this works https://reactjs.org/docs/hooks-reference.html#useref):

export const EmailTextField = props => {
  const { onStateChange } = props;
  const [state, setState] = useState({
    errors: [],
    onChange: false,
    pristine: true,
    touched: false,
    value: null
  });

  // `useRef` holds a mutable value that lasts as long
  // as the component, staying "more up to date" than the
  // current closure.
  const requestsInFlight = useRef(0);

  const helperText = "Email address will be used as your username.";

  const handleBlur = async event => {
    // Email Validation logic
    const emailAddress = event.target.value;
    const matches = event.target.value.match(
      `[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}`
    );
    if (matches) {
      // If there's a match, increment the ref for in-flight requests
      requestsInFlight.current += 1;

      await verifyUserNameAvailable(emailAddress);

      // After the response comes back, decrement the ref, and if
      // there are any requests still in flight, ignore that response.
      requestsInFlight.current -= 1;
      if (requestsInFlight.current > 0) {
        return;
      }
      const updatedState = {
        ...state,
        touched: true,
        value: emailAddress,
        errors: [emailAddress]
      };
      setState(updatedState);
      onStateChange(updatedState);
    } else {
      ...
    }
  };
like image 121
helloitsjoe Avatar answered Dec 22 '25 15:12

helloitsjoe