Manually show Loading/Spinner immediately in onClick of button in React (without waiting for setter)

Viewed 578

I have a case where I can't follow the React standard of calling a setter, e.g. setLoading, and then checking whether or not to show a component on render, {loading && <Loading />.

I have a Submit button which when clicked should immediately show a spinner with no delay, but setters are asynchronous. Therefore I need to do it in onClick. When subsequent renders occur, they can read this status and show/hide spinners as usual, with {loading && <Loading />.

I was thinking of using document.getElementById('spinner').style.display = 'block' / 'none', but will that work with React renders that show/hide components? Is there a way to force-show a React component right away in JS?

<Button type="submit" 
        onClick={() => { 
            // Can't use this setter because it doesn't update right away
            // setIsLoading(true);

            // Is this a good idea? Will subsequent renders read this?
            document.getElementById('spinner').style.display = 'block';
       }}

UPDATE: No good solution has been found. To mitigate that small delay before the setter kicks in, we disable the Submit button using Formik's {isSubmitting}:

<Button type="button" disabled={isSubmitting}> Submit</Button>

That at least disables the button so you can't click it again. But immediately showing the spinner is difficult. Even if we force its visibility in onClick= that conflicts with React's own variables, so mixing a manual approach with React doesn't work to turn it off. We have to live with that very small delay.

1 Answers

in state add a property for spinner:

state = {
  spinnerClass: 'invisible'
}

or if you're using a functional component

import React, { useState } from 'react';

// function
let [spinnerClass, updateClass] = useState('invisible');

then set some css:

.invisible {
  display: none;
/* OR */
  visibility: hidden;
  opacity: 0;
}

then add a class to spinner

<Spinner className={this.state.spinnerClass} />
/ or
<Spinner className={spinnerClass} />

and a function

changeClass = () => {
  this.setState({ spinnerClass: '' });
// or
  changeClass('');
}

for the button add onclick

<button onClick={changeClass} />
Related