Input validation setTimeout in ReactJS

Question!

I have a component that renders with className="error" or classname="" depending on whether the input is valid or not. This way in CSS I can simply do .error { background: red; }. The validity of the input is determined by the isValidNumber(..) function. However, right now the problem I'm having is that the validation is too instantaneous. If the input is invalid it almost instantly renders with "error" class name which is an annoying UX issue. I would like to have a delay of some sort to not have the class be "error" so instantly, like maybe 0.5 seconds would be nice.

Demo of component. Input is valid on things like "2.3 billion", or "1 trillion", or "203239123", but not "2 sheeps" or "mountain". Github Repo

Here is my component so far. You can see that I tried using setTimeout with setState({ isValid: isValid }) as the function whenever the isValid is false.

export default class NumberInput extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value: "",
            isValid: false
        };
    }
    setIsValid(isValid) {
        this.setState({ isValid: isValid })
    }
    handleChange(event) {
        var value = event.target.value
        this.setState({ value: event.target.value })

        var isValid = isValidNumber(value)
        if (isValid === false) {
            setTimeout(this.setIsValid(isValid), 2000);
        } else {
            this.setIsValid(isValid)
        }
    }
    getClassName() {
        var className = ''
        var errorClass = ''

        // Generate error classes based on input validity.
        if (this.state.isValid) {
            errorClass = ''
        } else {
            errorClass = 'error'
        }

        className = 'number-input ' + errorClass
        return className
    }
    render() {
        return (
            <div>
                <input type="text" className={this.getClassName()} value={this.state.value} onChange={this.handleChange.bind(this)} placeholder="Enter a number"/>
                <RawNumber isValid={this.state.isValid} value={this.state.value} />
            </div>
        )
    }
}


Answers

I would just add a transition for the background-color property to the .error class.

.error {
  background-color: red;
  transition: background-color .5s ease;
}

If you want to delay the transition, you can just tack on a value to the end of the declaration. The following would delay the transition for 1 second:

.error {
  background-color: red;
  transition: background-color .5s ease 1s;
}

I just reread your question. If you want to do the delay in JS as well or instead of CSS, then you need to change your handleChange method to the following.

handleChange(event) {
    var value = event.target.value
    this.setState({ value: event.target.value })

    var isValid = isValidNumber(value)
    if (isValid === false) {
        setTimeout(this.setIsValid.bind(this, isValid), 2000);
    } else {
        this.setIsValid(isValid)
    }
}


You need to fix the following line of code:

if (isValid === false) {
    setTimeout(this.setIsValid(isValid), 2000);
}

What you are doing here, you are basically calling this.setIsValid instantly and passing to setTimeout the result of it. So the state is changed instantly.

What you WANT to do, you want to pass to setTimeout the function itself, not the result. To do it, you want to wrap this.setIsValid into the function wrapper, like this:

if (isValid === false) {
    setTimeout((function() {
        this.setIsValid(isValid);
    }).bind(this), 2000);
}


This video can help you solving your question :)
By: admin