简体   繁体   中英

How change value of checkbox checked using useState in React

I will pass a list of checkbox fields with attributes to my component, I would like to change the value of checked attribute as the person selects or not, but the most I could do was double the check value in array.

How can I change this specific key in my state?

This is my code:

export default function Combocheck({ data, title, id }) {
  const inputLabel = React.useRef(null);

  const [state, setState] = React.useState({
    checkbox: data.map((element) => {
      element.checked = false;
      return element;
    })
  });

  const changeState = name => event => {
    const newValue = !state.checkbox[name].checked;
    setState({ checkbox: [
        ...state.checkbox,
        state.checkbox[name] = {
          ...state.checkbox[name],
          checked: newValue
        }
      ]
    });
  }

  console.log(state);

  return (
    <div> 
      <Typography color='textSecondary'>{title}</Typography>
      <Divider />
      <FormGroup>
        {state.checkbox.map(({code, description, checked}, i) => {
          return <FormControlLabel control={
            <Checkbox
              key={code}
              checked={checked}
              onChange={changeState(i)}
              value={code}
              color="primary"
            />
          }
          key={code}
          label={description}
          />
        })}
      </FormGroup>
    </div>
  );
}

Can be see or edit here: https://stackblitz.com/edit/react-irhqw9?file=Hello.js

You're adding a checkbox item each time you update your state, Instead: consider mapping over your elements and only changing the one that's applicable:

const changeState = name => event => {
  setState({ 
    checkbox: state.checkbox.map((checkbox, i) => {
      return i !== name ? 
        checkbox : 
        { ...checkbox, checked: !checkbox.checked }
    }) 
  });
}

I'm also not a huge fan of using the element index as the name; I'm pretty sure you'll be just fine using the code (assuming it's unique), and then this doesn't need to be a higher order function:

const changeState = event => {
  setState({ 
    checkbox: state.checkbox.map((checkbox) => {
      return event.target.value !== checkbox.code ? 
        checkbox : 
        { ...checkbox, checked: !checkbox.checked }
    }) 
  });
}

and then in the Checkbox props:

onChange={changeState}

I prefer to use one useState function for each checkbox.

In the 'old days' before hooks, each component had only one state - and the setState function would merge that state with the object passed to setState . Hooks & useState does not do it that way. You can have many useState hooks in one component, and the state does not merge.

Here is some code you can paste into App.js created using create-react-app to show what I mean:

App.js

import React, { useState } from 'react';

function App() {
  var [ firstCheckbox, setFirstCheckbox ]   = useState(true); // true - first check box defaults to checked.
  var [ secondCheckbox, setSecondCheckbox ] = useState(false); // false = second check box defaults to un-checked.
  return (
    <div style={{display:'flex', flexDirection: 'column'}}>
        <label for='first-check-box'><input type="checkbox" checked={firstCheckbox} onChange={() => setFirstCheckbox(!firstCheckbox)}></input>First Check Box</label>
        <label for='second-check-box'><input type="checkbox" checked={secondCheckbox} onChange={() => setSecondCheckbox(!secondCheckbox)}></input>Second Check Box</label>
    </div>
  );
}

export default App;

Another hint for make the chrome developer tools more useful: When you use many useState hooks as I suggest, it can be hard to see which state hook is which. Each of the useState hooks just shows as useState: true without any labels. The following code identifies the useState hook in the Components tab of the developer tools by making the actual state a property of an object, with the key being a label to show in the chrome developer tools.

App.js:

import React, { useState } from 'react';

function App() {
  var [ firstCheckbox, setFirstCheckbox ]   = useState({firstCheckBox: true});
  var [ secondCheckbox, setSecondCheckbox ] = useState({secondCheckBox: false});
  return (
    <div style={{display:'flex', flexDirection: 'column'}}>
        <label htmlFor='first-check-box'>
            <input type="checkbox"
                checked={firstCheckbox.firstCheckBox}
                onChange={() => setFirstCheckbox(
                    {firstCheckBox: !firstCheckbox.firstCheckBox}
                )}>
            </input>
            First Check Box
        </label>
        <label htmlFor='second-check-box'>
            <input
                type="checkbox"
                checked={secondCheckbox.secondCheckBox}
                onChange={() => setSecondCheckbox(
                    {secondCheckBox: !secondCheckbox.secondCheckBox}
                )}>
            </input>
            Second Check Box
        </label>
    </div>
  );
}

export default App;

Above, I used the input element's onChange() event for clarity. This requires the user to click on the actual check box. I prefer to allow them to click anywhere on the label OR the checkbox. To do this we can use the label element's onClick event:

To prevent react complaining in the console about the inputs not being controller, I mark them as readOnly also.

App.js:

import React, { useState } from 'react';

function App() {
  var [ firstCheckbox, setFirstCheckbox ]   = useState({firstCheckBox: true});
  var [ secondCheckbox, setSecondCheckbox ] = useState({secondCheckBox: false});
  return (
    <div style={{display:'flex', flexDirection: 'column'}}>
        <label
            htmlFor='first-check-box'
            onClick={() => setFirstCheckbox({firstCheckBox: !firstCheckbox.firstCheckBox})}>
            <input type="checkbox" checked={firstCheckbox.firstCheckBox} readOnly/>
            First Check Box
        </label>
        <label
            htmlFor='second-check-box'
            onClick={() => setSecondCheckbox({secondCheckBox: !secondCheckbox.secondCheckBox})}>
            <input type="checkbox" checked={secondCheckbox.secondCheckBox} readOnly/>
            Second Check Box
        </label>
    </div>
  );
}

export default App;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM