0

I have a situation where I want to append a class called shrink to the label text below (Display Name) when I click or type in the input box. My code is below:

const FormInput = ({ label, ...otherProps} ) => {
    let labelClassName = 'formInput-label';

    const addLabelClassName = () => {
        labelClassName = `${labelClassName} shrink`;
        console.log(labelClassName, 'labelClassName inside')
    }

    console.log(labelClassName, 'labelClassName outside')

    return (
        <div className="group">
            {label && 
                <label 
                    className={labelClassName}
                >{label}</label>
            }
            <input onFocus={addLabelClassName } onChange={addLabelClassName } className="formInput" {...otherProps} />
        </div>
    )
};

My question:

  1. Why does when I focus/ type, at first, React outputs the correct classnames for labelClassName inside as formInput-label shrink, but immediately changes it back to formInput-label at the labelClassName outside position? How would I fix this?

  2. I have also tried to change the code to using the UseState approach like below:

const FormInput = ({ label, ...otherProps} ) => {
    const [interaction, setInteraction] = useState('');
    let labelClassName = 'formInput-label';

    const onInteracting = () => {
        setInteraction('interacting')
    }

    if(interaction === 'interacting') {
        labelClassName = `${labelClassName} shrink`;
    }

    return (
        <div className="group">
            {label && 
                <label 
                    className={labelClassName}
                >{label}</label>
            }
            <input onFocus={onInteracting} onChange={onInteracting} className="formInput" {...otherProps} />
        </div>
    )
};

And this will append the correct class shrink to labelClassName but I'm not able to take that off when I click outside of the input/form. How may I fix this?

Thank you a ton!

1 Answers1

1

The second approach is a better way because with changing state you will trigger component rerendering (the first approach will never re-render component).

In the second approach, you can take advantage of onBlur event and create a handler which will set the state to the default value. Something like this. You don't need onChange to setIntercation

...
 const handleBlur = () => {
    setInteraction("");
  };
...

and then in input, you have to set up onBlur prop. onChange should not do the same thing as onFocus already does.

....
 <input
        onFocus={onInteracting}
        onBlur={handleBlur}
        className="formInput"
        {...otherProps}
      />
 ....

Alen Šljivar
  • 334
  • 2
  • 9
  • Thank you!!! This solves my problem. So if I understand correctly, in my first method, I append a `shrink` class but this didn't trigger re-rendering so the class never gets applied the second time? My follow-up question is why did React console.log out `labelClassName inside` then follows by `labelClassName outside`? Because when I look at those console logs, I thought that both are executed chronologically and didn't know that the component did not rerender. – Heymanyoulookkindacool Jun 05 '22 at 18:49
  • NP I am glad I could help you. `labelClassName outside` logs only on component mounting(first render) and that's it. ` labelClassName inside` logs every time onChange is triggered (every keystroke) but you have to be aware that rerender is never triggered and that's why `labelClassName outside` is never logged again. – Alen Šljivar Jun 05 '22 at 19:02
  • The only reason why input element displays good value inside is that your input component is uncontrolled. Read this answer and I believe you will get the idea behind this concept https://stackoverflow.com/a/68352693/5799742 – Alen Šljivar Jun 05 '22 at 19:06