dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

react-autosuggestでサジェスト(Autocomplete)項目を作る

Google 検索や乗換検索サービスとかでよく見るこれ。サジェストと呼ぶのかオートコンプリートと呼ぶのか。

f:id:dackdive:20170606033420g:plain

react-autosuggest というライブラリを使って実装してみます。
Codepen にサンプルコードがたくさんあります。

https://codepen.io/collection/DkkYaQ/


React のみの場合

Basic Usage のコードを参考に。

import React from 'react';
import { render } from 'react-dom';
import Autosuggest from 'react-autosuggest';

import './styles/index.scss';

// サジェストに表示する項目
const languages = [
  {
    name: 'C',
    year: 1972
  },
  ...
];


// 入力値に対するサジェスト項目を取得するロジック
const getSuggestions = (value) => {
  const inputValue = value.trim().toLowerCase();
  const inputLength = inputValue.length;

  return inputLength === 0 ? [] : languages.filter((lang) =>
    lang.name.toLowerCase().slice(0, inputLength) === inputValue,
  );
};

// サジェスト項目が Object の場合、サジェスト選択時に Object のどの項目を表示するか決める
const getSuggestionValue = (suggestion) => suggestion.name;

// サジェスト部分のレンダリングロジック
const renderSuggestion = (suggestion) => (
  <div>
    {suggestion.name}
  </div>
);

class Example extends React.Component {
  constructor() {
    super();

    // Autosuggest is a controlled component.
    // This means that you need to provide an input value
    // and an onChange handler that updates this value (see below).
    // Suggestions also need to be provided to the Autosuggest,
    // and they are initially empty because the Autosuggest is closed.
    this.state = {
      value: '',
      suggestions: [],
    };
  }

  onChange = (event, { newValue }) => {
    this.setState({
      value: newValue,
    });
  };

  // Autosuggest will call this function every time you need to update suggestions.
  // You already implemented this logic above, so just use it.
  onSuggestionsFetchRequested = ({ value }) => {
    console.log('onSuggestionsFetchRequested');
    this.setState({
      suggestions: getSuggestions(value),
    });
  };

  // Autosuggest will call this function every time you need to clear suggestions.
  onSuggestionsClearRequested = () => {
    console.log('onSuggestionsClearRequested');
    this.setState({
      suggestions: [],
    });
  };

  render() {
    const { value, suggestions } = this.state;

    // Autosuggest will pass through all these props to the input element.
    const inputProps = {
      placeholder: 'Type a programming language',
      value,
      onChange: this.onChange,
    };

    // Finally, render it!
    return (
      <Autosuggest
        suggestions={suggestions}
        onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
        onSuggestionsClearRequested={this.onSuggestionsClearRequested}
        getSuggestionValue={getSuggestionValue}
        renderSuggestion={renderSuggestion}
        inputProps={inputProps}
      />
    );
  }
}

render(
  <Example />,
  document.getElementById('root'),
);

NOTE:スタイルを指定しないとキャプチャのように <ul><li> のリスト表示になるので
https://codepen.io/moroshko/pen/XRgbxR を参考にスタイルを当てる必要がある。

f:id:dackdive:20170606034840p:plain


必須 props

以下、<Autosuggest> に必ず渡す必要があるデータや関数。

なお、便宜的にサジェスト項目として扱う Object を suggestion と表現する。↑の例だと

  {
    name: 'C',
    year: 1972
  },

がサジェスト項目1個分に相当する。


- suggestions: Array<suggestion>

文字通り、サジェストに表示するデータのリスト。 Array であれば良い。


- getSuggestionValue(): Function(suggestion) -> String

サジェスト項目がクリックされたとき、または上下カーソルキーでサジェスト内容を選択したときに
input 項目に表示するテキストを決めるロジックになる。

戻り値は String じゃないといけない。


- renderSuggestion(): Function(suggestion) -> JSX

サジェスト項目をどのように render するか。


- onSuggestionsFetchRequested(): Function(現在の入力値)

ドキュメントには

Will be called every time you need to recalculate suggestions.

と書かれており、発火タイミングがはっきりしないけど、入力テキストが変更されたときに呼ばれる関数。
基本的にはここで state で管理している suggestions をアップデートする。


- onSuggestionsClearRequested(): Function()

こちらも発火タイミングが謎。サジェストを非表示にしたときに呼ばれる関数。
ここで suggestions[] をセットするなどしてクリアーする。


- inputProps: Object

内部的に使われている <input> 項目へ渡す props。value, onChange などは基本必須になるかと。


サジェスト結果をセクションに分ける

サジェスト結果をカテゴライズして表示したいということがある。Yahoo!路線情報 のような。

f:id:dackdive:20170606041335p:plain:w160

この場合、suggestions として渡すデータは

const languages = [
  {
    title: '1970s',
    languages: [
      {
        name: 'C',
        year: 1972
      }
    ]
  },
  ...
];

というように一段ネストが深くなる。
title がセクションタイトル、languages がそのセクションにおける suggestions となる。

新たに以下の props を指定する。


- multiSection: bool

true にする。


- renderSectionTitle(): Function(section) -> JSX

セクションのタイトルの render ロジック。
↑の例で title を指定する場合は

function renderSectionTitle(section) {
  return (
    <strong>{section.title}</strong>
  );
}


- getSectionSuggestions(): Function(section) -> suggestions

セクションごとの suggestions を取得するロジック。
↑の例の場合は

function getSectionSuggestions(section) {
  return section.languages;
}


Redux と組み合わせる

https://github.com/zaki-yama/react-autosuggest-sample を参照。
基本的に value, suggestions を Redux の state で管理し、onChange, onSuggestionsFetchRequested, onSuggestionsClearRequested のタイミングで action を dispatch することになる。