0

Currently, I have many checkboxes which all obviously correlate to boolean values that need to be passed on onSubmit. I had something like this initially:

  const [value, setValue] = React.useState({
    alpha: true,
    beta: false,
    charlie: false,
    // twenty or so more
  });

  ...

  <CheckboxComp
    aria-label="id"
    checked={value.alpha}
    onChange={() => setValue({ alpha: !value.alpha})}
    label="id"
  />

Which was throwing me:

Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. 

Then I came across this.

- Through Input

        const [state, setState] = useState({ fName: "", lName: "" });
        const handleChange = e => {
            const { name, value } = e.target;
            setState(prevState => ({
                ...prevState,
                [name]: value
            }));
        };

        <input
            value={state.fName}
            type="text"
            onChange={handleChange}
            name="fName"
        />
        <input
            value={state.lName}
            type="text"
            onChange={handleChange}
            name="lName"
        />
   ***************************

 - Through onSubmit or button click
    
        setState(prevState => ({
            ...prevState,
            fName: 'your updated value here'
         }));

Which is nearly as close as I got but I was having trouble toggling true/false with something that was meant for strings on input. Any help, I know it's a silly question but it's a lot better than continuing onwards with useState(false) x20, the furthest thing from optimized.

Now I have something like this!

  const [value, setValue] = React.useState({
    alpha: true,
    beta: false,
    charlie: false,
  });

  const handleChange = e => {
      const { name, value } = e.target;
      setValue(prevState => ({
          ...prevState,
          [name]: value
      }));
  };

  console.log(value)
  
  ...

  <CheckboxComp
    aria-label="id"
    checked={value.alpha}
    onChange={handleChange}
    name="alpha"
    label="id"
  />

but console.log shows

alpha: "true"
  • Does this answer your question? [React - changing an uncontrolled input](https://stackoverflow.com/questions/37427508/react-changing-an-uncontrolled-input). Also your onChange call `() => setValue({ alpha: !value.alpha})` overwrites all other properties but `alpha`. – pilchard Jun 22 '21 at 10:33

2 Answers2

0

The problem is in this block of code of Checkbox component:

    onChange={() => setValue({ alpha: !value.alpha})}

When you are updating the state by passing { alpha: !value.alpha }, it is effectively removing all other keys from the object as you are modifying the object instead of modifying the particular value of a key while keep all others same.

You should use spread operator or Object.assign to provide the new object after modification.

For your current code, when setValue is fired, your state object is left with { alpha: false }, when it should be { alpha: false, beta: false, ...}

Saurish Kar
  • 576
  • 7
  • 13
0

As I pointed out in my comment, your setValue call is overwriting all properties but the explicitly named one. The example you showed passing a callback to setValue is not 'just for strings` it's simply a means of assigning a new value to a single property while maintaining all other properties. see: destructuring assignment and spread syntax (...).

Here is a quick working snippet.

function App() {
  const [value, setValue] = React.useState({
    alpha: true,
    beta: false,
    charlie: false,
    // twenty or so more
  });

  const checkChange = (e) => {
    const { id, checked } = e.target;
    setValue(prevValue => ({ ...prevValue, [id]: checked }))
  };

  return (
    <div>
      <form>
        {Object.keys(value).map(k =>
          <div key={k}>
            <input type="checkbox" id={k} name={k} checked={value[k]} onChange={checkChange} />
            <label for={k}>{k}</label>
          </div>
        )}
      </form>
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="root"></div>
pilchard
  • 12,414
  • 5
  • 11
  • 23
  • lets say instead of mapping out each checkbox I was to manually write each one, what changes would I need to make? –  Jun 22 '21 at 10:59
  • Replace all the `k`s with the property name of the state value. eg. `` – pilchard Jun 22 '21 at 11:02