🤧

react-selectで全選択とか独自UIの実装 & filterOptionのコントロール

2020/10/13に公開

react-selectで全選択の実装

react-selectで独自のボタンと埋め込む時の備忘録

こんなのをしたい

全選択とか全解除で複数選択を楽させたい

スクリーンショット 2020-10-12 18.38.19.png

成果物

codesandbox

ソース

import React, { useState } from "react";
import "./styles.css";
import ReactSelect, { components, createFilter } from "react-select";

const options = [
  { value: "ocean1", label: "Ocean", color: "#00B8D9" },
  { value: "blue", label: "Blue", color: "#0052CC" },
  { value: "purple", label: "Purple", color: "#5243AA" },
  { value: "red", label: "Red", color: "#FF5630" },
  { value: "orange", label: "Orange", color: "#FF8B00" },
  { value: "yellow", label: "Yellow", color: "#FFC400" },
  { value: "green", label: "Green", color: "#36B37E" },
  { value: "forest", label: "Forest", color: "#00875A" },
  { value: "slate", label: "Slate", color: "#253858" },
  { value: "silver", label: "Silver", color: "#666666" }
];

const widget = {
  label: "Hoge",
  value: "*"
};

export default function App() {
  const [selected, setSelected] = useState([]);

  const Option = (props) => {
    //  値が*の時は独自のボタンとかを描画
    if (props.data.value === "*") {
      return (
        <div style={{ display: "flex", justifyContent: "space-around" }}>
          <button
            onClick={() => {
              setSelected(options);
            }}
          >
            全選択
          </button>
          <button
            onClick={() => {
              setSelected([]);
            }}
          >
            全解除
          </button>
        </div>
      );
    }

    return <components.Option {...props} />;
  };

  // 独自UIはfIlterの対象外に
  const withOutWidgetFilter = (candidate, input) => {
    if (input && candidate.value === "*") {
      return true;
    }

    return createFilter(null)(candidate, input);
  };

  return (
    <div className="App">
      <h1>React Select With Widgets Example</h1>

      <div style={{ width: "500px", margin: "auto" }}>
        <ReactSelect
          value={selected}
          options={[widget, ...options]}
          components={{ Option }}
          isMulti
          closeMenuOnSelect={false}
          hideSelectedOptions={false}
          filterOption={withOutWidgetFilter}
          onChange={(value) => {
            setSelected(value);
          }}
        />
      </div>
    </div>
  );
}

解説

肝としては、optionsを拡張して独自の機能を埋め込んでいる

  const Option = (props) => {
    // 値が*の時は独自のボタンとかを描画
    if (props.data.value === "*") {
      return (
        <div style={{ display: "flex", justifyContent: "space-around" }}>
          <button
            onClick={() => {
              setSelected(options);
            }}
          >
            全選択
          </button>
          <button
            onClick={() => {
              setSelected([]);
            }}
          >
            全解除
          </button>
        </div>
      );
    }

    return <components.Option {...props} />;
  };

react-selectの絞り込み機能で埋め込んだUIを絞り込みの対象にしたないときはこうする

  const withOutWidgetFilter = (candidate, input) => {
    // 独自UI用の値の時はtrueを返しfilteringの対象外に
    if (input && candidate.value === "*") {
      return true;
    }

    // デフォルトの絞り込み処理
    return createFilter(null)(candidate, input);
  };

ざっくりとこんな感じで実現可能です
絞り込んだ値に対しての全選択とかはもう少し処理が必要ですが、ベースはこんな感じです

おまけ

Filterで絞り込まれている値ははSelectのrefから参照できます

const ref = React.useRef(null);

// 絞り込まれた値が取得できる
const filtedOptions = ref.current?.select.state.menuOptions.render || []

return <Select
        ref={ref}
        {...props}
       />

Discussion