score:0


export default function input(props) {
    const {
        name,
        ischecked,
        onchange,
        index,
    } = props;

    return (
        <>
            <input
                classname="popup-cookie__input"
                id={name}
                type="checkbox"
                name={name}
                checked={ischecked}
                onchange={onchange}
                data-action={index}
            />
            <label htmlfor={name} classname="popup-cookie__label">{name}</label>
        </>
    );
}

const checkboxeslist = [
    {name: 'essential', ischecked: true},
    {name: 'statistics', ischecked: false},
    {name: 'advertising', ischecked: false},
];

export default function checkboxeslist() {
    const [checkeditems, setcheckeditems] = usestate(checkboxeslist);

    const handlechange = (event) => {
        const newcheckboxes = [...checkeditems];
        newcheckboxes[event.target.dataset.action].ischecked = event.target.checked;
        setcheckeditems(newcheckboxes);
        console.log('checkeditems: ', checkeditems);
    };

    return (
        <ul classname="popup-cookie-checkbox-list">
            {checkboxeslist.map((checkbox, index) => (
                <li classname="popup-cookie-checkbox-list__item" key={checkbox.name}>
                    <input
                        id={checkbox.name}
                        name={checkbox.name}
                        ischecked={checkbox.ischecked}
                        onchange={handlechange}
                        index={index}
                    />
                </li>
            ))}
        </ul>
    );
}```

score:1

as an alternative to map, you might consider using a set. then you don't have to worry about initially setting every item to false to mean unchecked. a quick poc:

    const [selecteditems, setselecteditems] = usestate(new set())

    function handlecheckboxchange(itemkey: string) {
        // first, make a copy of the original set rather than mutating the original
        const newselecteditems = new set(selecteditems)
        if (!newselecteditems.has(itemkey)) {
            newselecteditems.add(itemkey)
        } else {
            newselecteditems.delete(itemkey)
        }
        setselecteditems(newselecteditems)
    }

...

    <input
        type="checkbox"
        checked={selecteditems.has(item.key)}
        onchange={() => handlecheckboxchange(item.key)}
    />

score:2

seems a bit of a long way round but if you spread the map out and apply it to a new map your component will re-render. i think using a object reference instead of a map would work best here.

const {usestate} = react

const mapper = () => {
  const [map, setmap] = usestate(new map());

  const addtomap = () => {
    const rndm = math.random().tofixed(5)
    map.set(`foo${rndm}`, `bar${rndm}`);
    setmap(new map([...map]));
  }

  return (
    <div>
      <ul>
        {[...map].map(([v, k]) => (
          <li key={k}>
            {k} : {v}
          </li>
        ))}
      </ul>
      <button onclick={addtomap}>add to map</button>
    </div>
  );
};

const rootelement = document.getelementbyid("react");
reactdom.render(<mapper />, rootelement);
<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="react"></div>

score:2

as a supplement to using a single object to hold the state of numerous items, the updates will not occur as expected if updating multiple items within a single render. newer commits within the render cycle will simply overwrite previous commits.

the solution to this is to batch up all the changes in a single object and commit them all at once, like so:

// an object that will hold multiple states
const [mystates, setmystates] = usestate({});

// an object that will batch all the desired updates
const statesupdates = {};

// set all the updates...
statesupdates[state1] = true;
statesupdates[state2] = false;
// etc...

// create a new state object and commit it
setmystates(object.assign({}, mystates, statesupdates));

score:33

i don't think using a map to represent the state is the best idea.
i have implemented your example using a plain object and it works:

https://codesandbox.io/s/react-hooks-usestate-xzvq5

const checkboxexample = () => {
  const [checkeditems, setcheckeditems] = usestate({}); //plain object as state

  const handlechange = (event) => {
      // updating an object instead of a map
      setcheckeditems({...checkeditems, [event.target.name] : event.target.checked });
  }

  useeffect(() => {
    console.log("checkeditems: ", checkeditems);
  }, [checkeditems]);  

  const checkboxes = [
      {
          name: 'check-box-1',
          key: 'checkbox1',
          label: 'check box 1',
      },
      {
          name: 'check-box-2',
          key: 'checkbox2',
          label: 'check box 2',
      }
  ];


  return (
      <div>
          <lable>checked item name : {checkeditems["check-box-1"]} </lable> <br/>
          {
              checkboxes.map(item => (
                  <label key={item.key}>
                      {item.name}
                      <checkbox name={item.name} checked={checkeditems[item.name]} onchange={handlechange} />
                  </label>
              ))
          }
      </div>
  );
}

edit:

turns out a map can work as the state value, but to trigger a re-render you need to replace the map with a new one instead of simply mutating it, which is not picked by react, i.e.:

const handlechange = (event) => {
  // mutate the current map
  checkeditems.set(event.target.name, event.target.checked)
  // update the state by creating a new map
  setcheckeditems(new map(checkeditems) );
  console.log("checkeditems: ", checkeditems);
}

but in this case, i think there is no benefit to using a map other than maybe cleaner syntax with .get() and .set() instead of x[y].


Related Query

More Query from same tag