見出し画像

Ant Design 5系のチェックボックスで、A,B,Cを選んだらDを外し、Dを選んだらA,B,Cを外す処理を組み込む

#adventcalendar #antd #react #typescript


本記事は、Japan Digital Design Advent Calendar 2023 の20日目の記事になります。

三菱UFJフィナンシャル・グループ(以下MUFG)の戦略子会社であるJapan Digital Design(以下JDD)でフロントエンドエンジニアをしている小笠原です。

本記事では、オープンソースCSSフレームワークの Ant Design を用いた時の Tips についてご紹介させていただきます。


はじめに

Ant Design のドキュメントを眺めると、APIに対応したサンプルコードや説明のみ掲載されています。しかし、様々な要件が発生する中で、APIに未対応であることはしばしば起こります。今回は、APIに未対応だった時にどういう解決をするか、実際に Ant Design の Checkbox を例にして Tips として記事を公開したいと思います。

Ant Design とは?

アリババ社によって開発されたデザインシステムです。Ant Design というのはアリのように小さいけど効率的に動作してくれる、個々は小さいが群れると強力な結果をもたらす、ようなイメージで名付けたのかなと思っています。

ライブラリで用いられる用語の表記については、公式に記載がありましたので紹介しておきます。

  • Ant Design

  • antd: React の UIライブラリ用

  • ant.design: website URL

環境について

  • react: 18.2.0

  • antd: 5.11.2

  • typescript: 5.0.2


ここからは、 Ant Design の Checkbox を例にして、API未対応の実装例をご紹介します。

本編

通常のチェックボックス

投資の種類を選ぶようなチェックボックスを実装します。コードは以下の通りです。

const options = [
  { label: "株式投資", value: "stock" },
  { label: "FX", value: "fx" },
  { label: "仮想通貨", value: "crypto" },
  { label: "金", value: "gold" },
  { label: "ETF", value: "etf" },
];

const App: React.FC = () => (
  <Checkbox.Group options={options} />
);
このようなUIが出来上がると思います。
チェックをすることができます。

このUIに対し、新しいケースを想定

「投資をしない」という選択肢を作ります。この選択肢には、他の選択肢と関連して以下の仕様を追加します。

  • 「投資をしない」をチェックした場合、他の選択肢のチェックを外す

  • 他の選択肢をチェックした場合、「投資をしない」のチェックを外す

このようなUIになります。
投資をしない、を選択した場合は他の選択肢を外します。
他の選択肢は同時に選択することができます。
const options = [
  { label: "株式投資", value: "stock" },
  { label: "FX", value: "fx" },
  { label: "仮想通貨", value: "crypto" },
  { label: "金", value: "gold" },
  { label: "ETF", value: "etf" },
  { label: "投資をしない", value: "none" }
];

const App: React.FC = () => (
  <Checkbox.Group options={options} />
);

UIを作ることは簡単です。上記のコードのように、 options に一つデータを追加すれば良いです。

ただし、ここから antd に options を渡しただけでは上記の仕様は満たせないので、 React#useState を用いて対応します。

const options = [
  { label: "株式投資", value: "stock" },
  { label: "FX", value: "fx" },
  { label: "仮想通貨", value: "crypto" },
  { label: "金", value: "gold" },
  { label: "ETF", value: "etf" },
  { label: "投資をしない", value: "none" }
];

const App: React.FC = () => {
  const { value, onChange } = useHandler();
  return <Checkbox.Group options={options} value={value} onChange={onChange} />;
};

const useHandler = () => {
  const [value, setValue] = React.useState<CheckboxValueType[]>([]);

  const onChange = (checkedValue: CheckboxValueType[]) => {
    if (value.includes("none")) {
      const does = checkedValue.filter((v) => v !== "none");
      setValue(does);
    } else if (checkedValue.includes("none")) {
      setValue(["none"]);
    } else {
      setValue(checkedValue);
    }
  };

  return {
    value,
    onChange
  };
};

このコードでは、 none が選択されていたら他のチェックを外し、 none 以外が選ばれたら none をチェックから外す、という処理をしています。

応用編

実際は、UIの整合性やデータ管理のために、 Form.Item で管理していることが多く、上記のようなコードでは動きません。なので、 Form インスタンスの setFieldValue 関数を使って、データ管理を別にしてあげる必要があります。

const options = [
  { label: "株式投資", value: "stock" },
  { label: "FX", value: "fx" },
  { label: "仮想通貨", value: "crypto" },
  { label: "金", value: "gold" },
  { label: "ETF", value: "etf" },
  { label: "投資をしない", value: "none" }
];

const App: React.FC = () => {
  const [form] = useForm();
  const { onChange } = useHandler((v: CheckboxValueType[]) => {
    form.setFieldValue("investment", v);
  });
  return (
    <Form form={form}>
      <Form.Item name="investment">
        <Checkbox.Group options={options} onChange={onChange} />
      </Form.Item>
    </Form>
  );
};

const useHandler = (fn: (v: CheckboxValueType[]) => void) => {
  const [value, setValue] = React.useState<CheckboxValueType[]>([]);

  const onChange = (checkedValue: CheckboxValueType[]) => {
    if (value.includes("none")) {
      const does = checkedValue.filter((v) => v !== "none");
      setValue(does);
      fn(does);
    } else if (checkedValue.includes("none")) {
      setValue(["none"]);
      fn(["none"]);
    }
  };

  return {
    onChange
  };
};

このコードでは、 form#setFieldValue を用いて Form.Item で管理している 'investment' の値を onChange のタイミングで更新しています。
なお、 React#useState の value を Checkbox.Group の標準APIである value に直接与えてしまうと、 antd の Form の状態管理と競合して動作がおかしくなることがあるため、標準API の value は 使用しておりません。

Checkbox.Group の標準APIの value, antd の Form といった状態管理のやり方はプロジェクト次第かと思います。いずれのやり方でも、プロジェクトで一貫した作りにすることで、 Form のデータフローの理解と管理がしやすくなるでしょう。
(実際は、 Form.Item の要素は複数個並んでおり、この例より複雑な作りになっていると思われます。)

さいごに

実際のプロダクトでは、上記のようにやや複雑な作りになっていることが多く、ドキュメントには書かれていない部分を調査する必要がありそうなので、その手間を省く意味でもこちらの Tips を今回展開させていただきました。

最後までご覧いただきありがとうございました。


Japan Digital Design株式会社では、一緒に働いてくださる仲間を募集中です。カジュアル面談も実施しておりますので下記リンク先からお気軽にお問合せください。

この記事に関するお問い合わせはこちら

Technology & Development Division
Senior Engineer
Sinnosuke Ogasawara


この記事が気に入ったらサポートをしてみませんか?