1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React-Select】便利なセレクトボックスライブラリで、選択肢や入力フィールドをカスタムする方法

Posted at

Overview

  • react-selectで選択肢をカスタムするには formatOptionLabel 属性を使用する
  • 「ドロップダウンメニューの選択肢」と「選択後の値」を出し分けるには meta 引数を使用する

はじめに

react-select は、Reactでセレクトボックスを実装するためのライブラリです。
本記事では、ドロップダウンメニューの選択肢表示をカスタマイズする方法を紹介します。

参考画像

選択肢をカスタマイズする

src/components/CustomSelect.tsx
import Select from 'react-select';

interface Option {
  role: 'admin' | 'none';
  name: string;
}

const options: Option[] = [
  { role: 'admin', name: 'John' },
  { role: 'none', name: 'Alice' },
];

const CustomSelect = () => {
  const FormatOptionLabel = (data: Option) => {
    const isAdmin = data.role === 'admin';
    return (
      <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
        <div style={{ color: isAdmin ? 'blue' : 'black', width: '60px' }}>{isAdmin ? '管理者' : '一般'}</div>
        <div style={{ color: 'black' }}>{data.name}</div>
      </div>
    );
  };

  return (
    <Select
      options={options}
      formatOptionLabel={(data) => FormatOptionLabel(data)}
      getOptionValue={(option) => option.name} // 識別子
    />
  );
};

export default CustomSelect;

選択肢の型 & 選択肢を定義する

任意の型構造で大丈夫です。

interface Option {
  role: 'admin' | 'none';
  name: string;
}

const options: Option[] = [
  { role: 'admin', name: 'John' },
  { role: 'none', name: 'Alice' },
];

FormatOptionLabel 関数を作成する

今回は、管理者ユーザーのみ色を変更するようにしました。

詳しくは後述しますが、全てのkeyを使用する必要はありません。

const FormatOptionLabel = (data: Option) => {
    const isAdmin = data.role === 'admin';
    return (
      <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
        <div style={{ color: isAdmin ? 'blue' : 'black', width: '60px' }}>{isAdmin ? '管理者' : '一般'}</div>
        <div style={{ color: 'black' }}>{data.name}</div>
      </div>
    );
};

Select にプロパティを渡す

return (
    <Select
      options={options}
      formatOptionLabel={(data) => FormatOptionLabel(data)}
      getOptionValue={(option) => option.name} // 識別子
    />
)

getOptionValueプロパティ

デフォルトでは「選択肢のvalueプロパティ」によって「どの選択肢が選ばれているか」が識別されます。
今回は選択肢がvalueプロパティを持たないため、代わりにnameを使用するよう設定しています。

成功例 getOptionValueがない場合
成功例 失敗例
選択されたもののみ強調されている 全て強調されてしまう

選択後の表示をカスタマイズする

import Select, { FormatOptionLabelMeta, StylesConfig } from 'react-select';

interface Option {
  role: 'admin' | 'none';
  name: string;
}

const options: Option[] = [
  { role: 'admin', name: 'John' },
  { role: 'none', name: 'Alice' },
];

const customStyles: StylesConfig<Option, false> = {
  control: (provided) => ({
    ...provided,
    width: '200px', // コントロール(選択エリア)の幅を設定
  }),
};

const CustomSelect = () => {
  const FormatOptionLabel = (data: Option, meta: FormatOptionLabelMeta<Option>) => {
    const isAdmin = data.role === 'admin';
    return meta.context === 'menu' ? (
      <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
        <div style={{ color: isAdmin ? 'blue' : 'black', width: '60px' }}>{isAdmin ? '管理者' : '一般'}</div>
        <div style={{ color: 'black' }}>{data.name}</div>
      </div>
    ) : (
      <div style={{ color: 'black' }}>{data.name}</div>
    );
  };

  return <Select options={options} formatOptionLabel={FormatOptionLabel} getOptionValue={(option) => option.name} styles={customStyles} />;
};

export default CustomSelect;

meta を使用し、表示の出し分けを行う

先ほどとの違いはmetaを使用し、表示を出し分けている点です。
meta'menu''value'の2つの値を持ちます。
'menu'はドロップダウンメニューに対して、
'value'は選択後の表示に対して発火します。

1 2
スクリーンショット 2025-02-17 19.06.39.jpg スクリーンショット 2025-02-17 19.07.14.jpg
選択肢ではroleが表示されている 選択後は名前のみ表示
return meta.context === 'menu' ? (
      <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
        <div style={{ color: isAdmin ? 'blue' : 'black', width: '60px' }}>{isAdmin ? '管理者' : '一般'}</div>
        <div style={{ color: 'black' }}>{data.name}</div>
      </div>
    ) : (
      <div style={{ color: 'black' }}>{data.name}</div>
);

customStyleを適用

menu / value で表示を出し分けると、以下画像1のようなレイアウト崩れが発生します。
customStyleを適用することで、柔軟なレイアウトを実現可能です。

1 2
スクリーンショット 2025-02-17 19.06.15.jpg スクリーンショット 2025-02-17 19.12.07.jpg
menuのレイアウト崩れ コンパクトなセレクトボックス
const customStyles: StylesConfig<Option, false> = {
  control: (provided) => ({
    ...provided,
    width: '200px', // コントロール(選択エリア)の幅を設定
  }),
};

// ~~

return <Select 
  options={options} 
  formatOptionLabel={FormatOptionLabel} 
  getOptionValue={(option) => option.name} 
  styles={customStyles} />;

menuも設定する場合

const customStyles: StylesConfig<Option, false> = {
  menu: (provided) => ({
    ...provided,
    width: '200px', // メニューの幅を設定
  }),
  control: (provided) => ({
    ...provided,
    width: '100px', // コントロール(選択エリア)の幅を設定
  }),
};

おわりに

react-selectは(公式の情報が弱い節はありますが)とても便利なライブラリです。

ぜひ使ってみてください!

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?