Tailwind CSS+Reactの複数チェックボックス

2020-11-08 /Development
この記事は最終更新日から2年以上経過しています。

昨日 に続いて Tailwind CSS のカスタムフォームも触ってみました。カスタムフォームのCSSは別途プラグインを入れる必要があるんですね。 あとついでにReactで複数チェックボックスを扱う方法も調べてみてたんですが色々出てきて都度迷ってしまいそうなのでメモ。

インストール

コピーしました
$ yarn add @tailwindcss/custom-forms -D

プラグインを追加

コピーしました
// tailwind.config.js
module.exports = {
  // ...
  plugins: [require("@tailwindcss/custom-forms")],
};

コンポーネント側

コピーしました
import React, { useEffect, useState } from "react";

const CheckBox = (props) => {
  return (
    <>
      <input type="checkbox" className="form-checkbox" type="checkbox" id={props.name} name={props.name} checked={props.checked} onChange={props.onChange} value={props.value} />
      <label className="ml-2 text-sm" htmlFor={props.name}>
        {props.label}
      </label>
    </>
  );
};

const CheckBoxes = (props) => {
  const [checkedItems, setCheckedItems] = useState(props.checkedItems || []);

  const checkboxes = props.checkboxes || [
    {
      name: "checkbox_0",
      label: "item0",
      value: "0",
    },
  ];

  useEffect(() => {
    if (props.onUpdate) props.onUpdate({ checkedItems: checkedItems });
  }, [checkedItems]);

  const onChange = (e) => {
    if (e.target.checked) {
      setCheckedItems([...checkedItems, e.target.value]);
    } else {
      setCheckedItems(checkedItems.filter((i) => i !== e.target.value));
    }
  };

  const onReset = (e) => {
    setCheckedItems([]);
  };

  return (
    <>
      <fieldset className="my-2">
        <legend className="text-sm text-gray-700 mb-2">{props.legend || "Checkboxes"}</legend>
        {checkboxes.map((item) => {
          return (
            <div key={item.name} className="block items-center py-1">
              <CheckBox {...item} checked={checkedItems.includes(item.value)} onChange={onChange} />
            </div>
          );
        })}
        <div className="text-right">
          <button type="button" className="text-sm text-gray-500" onClick={onReset}>
            Reset
          </button>
        </div>
      </fieldset>
    </>
  );
};

export default CheckBoxes;

使う側

使い回ししたいので、onUpdate含めコンポーネントの中身は触らないで良いようにしておきました。

コピーしました
<CheckBoxes
  {...{
    onUpdate: onUpdateCheckboxes,
    legend: "Checkboxes",
    checkedItems: ["0", "1", "2"],
    checkboxes: [
      {
        name: "checkbox_0",
        label: "Item0",
        value: "0",
      },
      {
        name: "checkbox_1",
        label: "Item1",
        value: "1",
      },
      {
        name: "checkbox_2",
        label: "Item2",
        value: "2",
      },
    ],
  }}
/>

所感

ReactではVueのv-model的なものが無いので自前でなんとかしなければならなくてどう実装したらよいのか迷ったのですが、ちょっと調べてみたところ色々なやり方が出てきて悩ましいですね…

連想配列のcheckedItemsにスプレッド構文を使ってChangeイベントがあったものを { name: checked }みたいな感じで追加していく方法が比較的多くヒットしたのですが、これだと格納されているものには非アクティブなものが含まれているし全てのチェックボックスを触るまで抜けがある状態になってしまってなんだか違和感があるのでcheckedItemsを配列にして追加or削除をするようにしてみました。 APIに値を送信するような用途で使う時はアクティブなvalueだけが入った配列があると嬉しいので。

Comment
comments powered by Disqus
Profile

石原 悠 / Yu Ishihara

デザインとプログラミングと編み物とヨーグルトが好きです。