Strange Bugs 1: The Fussy Controlled Form

Welcome to Strange Bugs!

In this post I will go over a bug I encountered while creating a controlled form in the app I'm working on.

Here is the layout for the component I was building:

const Card = () => {
   const [showFront, setShowFront] = useState(true)
   const [formData, setFormData] = useState({
      input1: value1,
      input2: value2,
      ...
   })

   function handleInputChange(e) {
      const name = e.target.name;
      const value = e.target.value;
      setFormData({ ...formData, [name]: value });
   }
   ...

   const CardFront = () => {
      return <form>
         <input name="input1" value={formData.input1}
         <input name="input2" value={formData.input2}
         ...
         <button>Submit</button>
      </form>

   }

   const CardBack = () => {
      return <div>
         <p>{formdata.input1}</p>
         <p>{formdata.input2}</p>
         ...
      </div>

   }

   return (
      { showFront ? <CardFront /> : <CardBack />
   )

}

The Bug:

When attempting to type into one of the inputs, I was only able to enter one character, and then I would be tabbed out of the input, and would have to re click the input over and over to continue typing.

The Struggle:

This bug had me stumped for a good few hours, unsure what was causing the issue. After some time, I was convinced there was something going on with the state, as this issue had only came up after I turned the inputs into a controlled form by giving them state.

The Realization:

While taking a look and breaking things down line by line with a fellow peer, we finally discovered what was going on. On each keystroke while typing in one of the inputs, the state for the formData would be updated in the parent / wrapper component, which would re-render the child component, causing it to forget that we we're writing in one of the inputs.

The Solution:

My hack to fix this, was to simply break the nested components out of themselves, and put the jsx into react fragments instead.

return (
      { showFront ? 
         <> 
            <form>
               <input name="input1" value= {formData.input1}
               <input name="input2" value={formData.input2}
            ...
               <button>Submit</button>
            </form>
         </> 
         : 
         <> 
            <div>
               <p>{formdata.input1}</p>
               <p>{formdata.input2}</p>
               ...
            </div>
         </>
   )

14