LoginSignup
42

More than 3 years have passed since last update.

React hooks で複数のcheckboxを実装してみる

Last updated at Posted at 2020-07-06

useStateとuseEffectを使って複数のcheckboxを実装してみる

いろいろ手探りで書いてみたのでメモに残しておく。

仕様について

  • チェックボックスが一つ以上checkedになった場合のみ送信ボタンを表示させたい。
  • checkedアイテムのidを、送信ボタンが押されたタイミングで配列で送信したい。
checkBox.jsx
import React, { useState, useEffect } from "react"

//checkboxのvalueリスト
const checkLists = [
  "パン",
  "おにぎり",
  "焼き肉",
  "ラーメン",
  "たこ焼き",
  "アイスクリーム",
]

//checkboxコンポーネント
const CheckBox = ({id, value, checked, onChange}) => {
  return (
    <input
      id={id}
      type="checkbox"
      name="inputNames"
      checked={checked}
      onChange={onChange}
      value={value}
    />
  )
}

const CheckBoxList = () => {

//checkedItemsは初期値を空のオブジェクトにする
  const [checkedItems, setCheckedItems] = useState({})
//ひとつでもcheckedになっている場合にのみ送信ボタンを表示させたいので、全体のStateを監視する
  const [isBtnHide, setIsBtnHide] = useState(true)

  useEffect(() => {
//checkedItemsが空では無い場合、送信ボタンを表示させる
    Object.keys(checkedItems).length && setIsBtnHide(false)
//すべてのcheckedItemの値がfalseの場合に送信ボタンを表示させる
    setTimeout(() => {
      if (
        Object.values(checkedItems).every(checkedItem => {
          return checkedItem === false
        })
      ) {
        setIsBtnHide(true)
      }
    },100);
  }, [checkedItems])

  const handleChange = e => {
//checkedItemsのstateをセット
    setCheckedItems({
      ...checkedItems,
      [e.target.id]: e.target.checked
    })
    console.log('checkedItems:', checkedItems)
  }

  const dataSendBtn = e => {
//既定のイベントをキャンセルさせる
    e.preventDefault()
//送信ボタンを押したタイミングで、checkedItemsオブジェクトのvalueがtrueのkeyのみを配列にしてconsoleに表示させる
    const dataPushArray = Object.entries(checkedItems).reduce((pre,[key, value])=>{
      value && pre.push(key)
      return pre
    },[])
    console.log("dataPushArray:", dataPushArray)
  }

  return (
    <>
      <h2>好きな食べ物</h2>
      <form>
        {checkLists.map((item, index) => {
          index = index + 1
          return (
            <label htmlFor={`id_${index}`} key={`key_${index}`}>
              <CheckBox
                id={`id_${index}`}
                value={item}
                onChange={handleChange}
                checked={checkedItems[item.id]}
              />
              {item}
            </label>
          )
        })}
{/* checkedがない場合には送信ボタンを表示させない */}
        {!isBtnHide && <button onClick={dataSendBtn}>アンケート送信ボタン</button>}
      </form>
    </>
  )
}

export default CheckBoxList


とりあえず意図した通りの動きになった。
でもなんか遠回りしている気がする。。。。

new Map()での実装

 const [ischecked, toggleChecked] = useState(new Map())

Mapはstateを更新するため(再レンダーをトリガーする)に、Mapをnew Mapに置き換える必要がある。

const handleChange = e => {
  checkedItems.set(e.target.id, e.target.checked)
  setCheckedItems(new Map(checkedItems) );
  console.log("checkedItems: ", checkedItems);
}

20200712追記

forを使うのはイケてない。。。と指摘されましたので書き直しました

before
const dataPushArray = []
for (let [key, value] of Object.entries(checkedItems)) {
  if (value) {
    dataPushArray.push(key)
  }
}
console.log("dataPushArray:", dataPushArray)
after
const dataPushArray = Object.entries(checkedItems).reduce((pre,[key, value])=>{
  value && pre.push(key)
  return pre
},[])
console.log("dataPushArray:", dataPushArray)

もっと良い方法はないか、いろいろ考えてみる、追記していく :relieved:

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42