インクリメンタル検索で、入力と一致した部分文字列をハイライトする。
というのを、React Selectを使ってやる。
基本的には公式APIドキュメントを読んでいけば書いてあるはずの話だが、カスタムコンポーネントの部分はちょっとつまづいたのでメモ。
前提
- react v17.0.2
- react-select v4.3.0
React Select の基本的な使い方
インクリメンタル検索だけならあまり考えずに実装できる。
label
, value
(+その他必要なプロパティ)を持つオブジェクトをoptionとして渡すだけで、 label
に対して入力文字列での検索ができる。
自前で絞り込みロジックを実装したければ、 filterOption
プロパティが使える。その他いろいろ使えるプロパティはこちら参照。
import React from 'react'; import Select from "react-select"; export default function App() { const options = [ { label: "JavaScript", value: 1 }, { label: "TypeScript", value: 2 }, { label: "ECMAScript", value: 3 }, { label: "Java", value: 4 }, { label: "JScript", value: 5 } ]; return ( <div> <Select options={options} /> </div> ); }
optionコンポーネントを自前のものに差し替え
入力した文字列と一致した部分文字列を強調表示させる。
optionのCSSをいじるだけなら、 styles
で指定できるが、それだけでは難しいので、optionコンポーネントを自前のカスタムコンポーネントで差し替える。
↓こんな感じで Option コンポーネントを作って components
に指定する。
const Option = (props) => { return <div>...</div> } const options = [/*省略*/]; return ( <div> <Select options={options} components={{ Option }} /> </div> );
ただ、一から実装するのはめんどくさいので、必要なところ以外はデフォルト部品の実装を使いたい。
components.Option
でもとの部品が定義されているので、これを持ってくる。
//import Select, { components } from "react-select"; const Option = (props) => { //もとの部品をそのまま使った状態 return <components.Option {...props} /> }
この components.Option
は label
プロパティの値を表示するようになっているが、実は children
を取ることもできて、 children
があればそっちを描画するようになっているっぽい。
なので、 children
のところに表示したい内容を実装するだけで良い。
この Option が受け取る props の内容がドキュメントを見てもいまいち分からなかったのだけど、少なくとも
- data : options で渡したobject
- selectProps
- inputValue : 入力された文字列
が入ってくることはわかった。
なので↓こんなふうに書ける。
const Option = (props) => { const input = props.selectProps.inputValue; const label = props.data.label; if (input === "") { return <components.Option {...props} />; } const idx = label.toLowerCase().indexOf(input.toLowerCase()); const styles = { highlight: { fontWeight: "bold", color: "#ee0000" } }; return ( <components.Option {...props}> <div> <span>{label.slice(0, idx)}</span> <span style={styles.highlight}>{label.slice(idx, idx + input.length)}</span> <span>{label.slice(idx + input.length)}</span> </div> </components.Option> ); };
コード全体はこちら。