Saturday, April 30, 2022

React and uncontrolled component experiment

My project uses React for the client side code.  Normally, changes in the display options dropdown are made as soon as the user makes them so there is no need for "Cancel" or "Apply" buttons. There is, however, a section of input fields that must be canceled or applied as a set, which is where a recent bug appeared.

Environment:
react@17.0.1

The "Cancel" button (and closing of the dropdown) for the set of input fields was not restoring settings prior to the settings being applied.  This was confusing and erroneous because the user thinks the settings were applied even if they may not have been.

Let's name the parent component <Result> and the child component <DisplayOptions>.  The reason why this occurred is because the set of input elements were all React controlled components where state was stored in the parent <Result> component and an event handler was called for every state update.  

What needed to happen was the set of input elements needed to be saved to the parent as a set when the "Apply" button was clicked and when the "Cancel" button was clicked it needed to restore any settings that hadn't been applied.

There were two solutions that I looked into to solve this:   

  1. Change input elements to uncontrolled components.  After hitting apply, call a props function to save it back up to the parent.  
    • Outcome - I've noted down the reasons why I couldn't go with this solution:
      • If <DisplayOptions> had no child components that depended on the changing value of an uncontrolled component, I think it would work.  But if there is a child component that is dependent on an uncontrolled component's value changing, there's no way to force a prop or state change for the child component to re-render.
      • Extra care is needed to keep track of checkbox values with input[inputReferenceName].current.checked and text values with input[inputReferenceName].current.value.
      • For any values that are inherently a number, the uncontrolled component's current value is always a string.  So parsing to an int would need to happen each time its value is needed.
      • Only after the initial render will any form input elements have the handle to the React reference declared in the component's constructor.  This made for extraneous code used throughout to either use the dropdown's component React reference to the input element's current value when it was defined, or the props value of the input element sent from the parent to set the initial value of the input elements.
  2. Keep the input elements React controlled, but have a local copy saved in state for the <DisplayOptions> component.  Each time <DisplayOptions> is opened the constructor is called, so this was the location to set the props values of the input elements sent from the parent to local state.  After clicking apply, call a props function to save it back up to the parent.  This way the next time <DisplayOptions> is opened, its constructor would be called again to set the current value of the input elements.
    • Outcome - This ended up being a simple solution and best solution for the use case.

No comments:

Post a Comment

I appreciate your time in leaving a comment!