react-selectのカスタマイズでつまづいた点まとめ(style中心)
概要
react-selectでは画像を選択肢に入れたセレクトボックスを作成することが可能です。
しかし、一部分でしかreact-selectを使用しない場合、react-selectを使用した箇所とそうでない箇所とで見た目の統一感を出す必要があるケースが存在するかと思います。
その時にstyleのカスタマイズでいくつかつまづいた点があり、それぞれの解決法や解決にあたって役立った記事などをまとめました。
react-selectカスタマイズの基本
基本的なカスタマイズについては公式情報および、waddy_uさんの以下の記事を参考にさせていただきました。
カスタマイズ可能な箇所の名称
カスタマイズ可能な箇所なのか?やその箇所の名称が分からず、困ることが数回ありました。
はてぶでtmegosさんが書かれていた、style keyの一覧およびそれを分かりやすく表現したサンプルコードを参考にさせていただきました。
画像を選択肢に表示する
waddy_uさんがこちらで書かれています。
return (
<Select
+ formatOptionLabel={(option) => <FormatOptionLabel option={option} />}
/>
);
とした上で、別途FormatOptionLabel
の中身を書くことで、好きなように画像を表示できます。
今回自分の関わる案件では、とにかくシンプルな画像のみを表示するセレクトボックスが必要だったため、以下のようなコードを書きました。Next.jsを使用している環境だったため、img
ではなくImage
を使用しています。またTypescriptのため型の指定をしています。
// eslint-disable-next-line react/display-name
export const FormatOptionLabel = memo(({ option }: { option: sampleParam }) => (
<Image
src={`/imgs/sample${option.value}.png`}
alt={option.value}
width={300}
height={30}
referrerPolicy='no-referrer'
/>
))
※脱線ですが、Next.jsでImage
を使用する場合、画像ファイルはpublic
フォルダの下に置く必要があります。
今回はimgs
フォルダを作成し、public/imgs/sample0.png
というように、option.value
に対応するファイル名の画像を作成しました。
あの縦線を消す方法
react-selectのデフォルトのstyleで特徴的な、あの縦線を消す方法です。
あの縦線(赤枠部分)
あの縦線はstyle keyでindicatorSeparator
と呼ばれます。
でwaddy_uさんも書かれていますが、
return (
<Select
+ components={{
+ IndicatorSeparator: () => null,
+ }}
/>
);
で消すことができます。
styleをどこで書いていくか?
ガラッと全体を書き換えたいケースと、基本はreact-selectのデフォルトを使用したいケースと、色々あると思います。
react-selectでは、
- styles prop
- classNames prop
- classNamePrefix prop
の3つを使うことができます。
https://react-select.com/styles
以下に実際に試した3つのパターンをまとめました。stylesを使う手法(1・3)とclassName,classNamesを使う手法(2・4)を組み合わせることも可能です。
自分の場合は最終的に、1と4の組み合わせで使用しました(基本は1で完結させるつもりでしたが、isFocusedの指定でtailwindの記法を使用したかったため)。
1:あちこち編集したい/定数で別途指定する
return(
<Select
+ styles={customStyles}
/>
);
で書いていくことができます。
以下はそのカスタム例です。
import Select from 'react-select';
const customStyles = {
control: (provided, state) => ({
...provided,
width: '100%',
borderRadius: 'none',
border: '1px solid gray',
backgroundColor: '#eee',
textAlign: 'center',
'&:hover': {
border: '2px solid red',
cursor: 'pointer',
},
}),
option: (provided, state) => ({
...provided,
backgroundColor: state.isSelected ? '#f00' : '#eee',
color: state.isSelected ? 'white' : 'black',
'&:hover': {
backgroundColor: '#f88',
},
}),
menu: (provided) => ({
...provided,
backgroundColor: '#eee',
}),
singleValue: (provided, state) => ({
...provided,
color: state.isSelected ? 'white' : 'black',
}),
};
~~~(中略)~~~
<Select styles={customStyles} />
2:あちこち編集したい/別のCSSに切り出す
この方法ではTailwind CSSなどを組み合わせることが可能です。
import Select from 'react-select';
import 'tailwindcss/tailwind.css';
import './customStyles.css';
~~~(中略)~~~
<Select className="select-box" />
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
.select-box {
@apply w-full rounded-none border-2;
& .Select__control {
@apply rounded-none;
}
& .Select__option {
@apply bg-white text-black;
}
& .Select__option--is-selected {
@apply bg-blue-500 text-white;
}
& .Select__option--is-focused {
@apply bg-gray-100;
}
& .Select__single-value {
@apply text-black;
}
& .Select__indicator-separator {
@apply hidden;
}
& .Select__dropdown-indicator {
@apply text-black;
}
& .Select__placeholder {
@apply text-gray-500;
}
}
3:少しだけ変えたい/Selectのstylesに直書き
基本はbaseStylesを踏襲し、細かなところだけ変更する際に使える方法です。
<Select
styles={{
control: (baseStyles) => ({
...baseStyles,
minWidth: 160,
}),
}}
/>
4:少しだけ変えたい/SelectのclassNamesに直書き
この方法ではTailwind CSSなどを組み合わせることが可能です。
以下の例ではセレクトボックスがフォーカスされた時に枠線で囲まれるようにしました。
import Select from 'react-select';
import 'tailwindcss/tailwind.css';
~~~(中略)~~~
<Select
classNames={{
control: (state) => (state.isFocused ? 'outline-none ring-2 ring-blue-500' : ''),
}}
/>
optionsの定義の仕方
react-selectのoptions
はvalue
とlabel
の二つの値が必須となっています。
以下に簡単なサンプルとして2つの方法を書きました。
1:基本は定数として別途定義する
const options = [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' },
{ value: 'option3', label: 'Option 3' }
];
<Select options={options} />
2:map()する
export const SAMPLE_TYPES = ['Apple', 'Banana', 'Cherry']
<Select
value={{ label: sample.value, value: sample.value }}
options={SAMPLE_TYPES.map((i) => ({
label: i.toString(),
value: i.toString(),
}))}
/>
最後に
react-selectは見た目も内容も細かくカスタマイズできるため便利ですが、一方で見た目のカスタマイズに関して中々細かな情報までは見つけにくく、触ってみると大変なところもありました。
自分自身はこの記事で触れた参考元の情報と、styleの書き換えをどこで行うべきなのか?が分かってから大分スムーズにやりたいことができるようになった印象です。
この記事が何かの参考になれば幸いです。
Discussion