Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement multiple checkbox using react hook

I want to implement multiple checkboxes on my HTML page using react-hook.

I tried implementing using this URL: https://medium.com/@Zh0uzi/my-concerns-with-react-hooks-6afda0acc672. In the provided link it is done using class component and working perfectly but whenever I am using React hook setCheckedItems to update checkbox checked status it's not re-rendering the view.

The very first time the view is rendering and console.log() is printing from Checkbox component. After clicking on checkbox function handleChange gets called and checkedItems updates the value but the view is not rendering again (no console.log() printing). And {checkedItems.get("check-box-1")} is also not printing any value.

Below is my sample code.

CheckboxExample :

import React, { useState } from 'react';
import Checkbox from '../helper/Checkbox';

const CheckboxExample = () => {
    const [checkedItems, setCheckedItems] = useState(new Map());

    const handleChange = (event) => {
        setCheckedItems(checkedItems => checkedItems.set(event.target.name, event.target.checked));
        console.log("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.get("check-box-1")} </lable> <br/>
            {
                checkboxes.map(item => (
                    <label key={item.key}>
                        {item.name}
                        <Checkbox name={item.name} checked={checkedItems.get(item.name)} onChange={handleChange} />
                    </label>
                ))
            }
        </div>
    );
}
export default Example;

Checkbox:

import React from 'react';

const Checkbox = ({ type = 'checkbox', name, checked = false, onChange }) => {
    console.log("Checkbox: ", name, checked);

  return (<input type={type} name={name} checked={checked} onChange={onChange} /> )
}
export default Checkbox;
like image 524
Nakesh Avatar asked May 23 '19 10:05

Nakesh


People also ask

How do you handle checkboxes in React Hooks?

The state can be changed by providing the new state value or an updater function. The updater function takes the current state and returns the new state. The state related to a checkbox input is a boolean. When the state is false the checkbox is unchecked, when the state is true the checkbox is checked.


2 Answers

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].

like image 114
pawel Avatar answered Sep 23 '22 17:09

pawel


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>
like image 27
Francis Leigh Avatar answered Sep 22 '22 17:09

Francis Leigh