react-autosuggest を React Hooks x TypeScript 上に入れてサジェスト機能を実装した

react-autosuggestのGitHubに書かれてるサンプルコードはクラスコンポーネントですが、Hooks勢であれば関数型に書き直したいと思うもの。

英語でも情報がなかったようなのでTypeScript込みで書き直してみた


## この記事の完成品
こんな感じで動くやつになる

![reactAutosuggest.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651103/833c44c0-65be-0067-b74c-e301bd0c3be2.gif)

## 【元ネタ】GtiHubのサンプルコード
元ネタはこれ。一番基本的な使い方
GitHub: https://github.com/moroshko/react-autosuggest#installation

```sample.jsx
import Autosuggest from 'react-autosuggest';

const languages = [
  {
    name: 'C',
    year: 1972
  },
  {
    name: 'Elm',
    year: 2012
  },
  ...
];

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
  );
};

const getSuggestionValue = suggestion => suggestion.name;

const renderSuggestion = suggestion => (
  <div>
    {suggestion.name}
  </div>
);

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

    this.state = {
      value: '',
      suggestions: []
    };
  }

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

  onSuggestionsFetchRequested = ({ value }) => {
    this.setState({
      suggestions: getSuggestions(value)
    });
  };

suggestions.
  onSuggestionsClearRequested = () => {
    this.setState({
      suggestions: []
    });
  };

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

    const inputProps = {
      placeholder: 'Type a programming language',
      value,
      onChange: this.onChange
    };

    return (
      <Autosuggest
        suggestions={suggestions}
        onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
        onSuggestionsClearRequested={this.onSuggestionsClearRequested}
        getSuggestionValue={getSuggestionValue}
        renderSuggestion={renderSuggestion}
        inputProps={inputProps}
      />
    );
  }
}
```

関数がいろいろあってややこしいライブラリだが基本的に１つの関数名（及びstate）と１つのpropsが対応していると見るとわかりやすい(getSuggests関数とonChange関数は別)

```sample.jsx
      <Autosuggest
        suggestions={suggestions} // suggestionsはサジェスト候補のオブジェクトが格納されたstate
        onSuggestionsFetchRequested={this.onSuggestionsFetchRequested} // suggestionsの状態を更新する関数 
        onSuggestionsClearRequested={this.onSuggestionsClearRequested} // suggestionsを初期状態にする関数
        getSuggestionValue={getSuggestionValue} // langagesのオブジェクトからnameを取り出す関数
        renderSuggestion={renderSuggestion} // サジェスト一覧表示に使うJSXを返す
        inputProps={inputProps} // placeholder, value, onChange の初期値を渡す
      />
```

## Hooks & TypeScript で置き換えた
型ファイルも一緒に落としておく

```
yarn add react-autosuggest @types/react-autosuggest
```


使うデータへLangTypeの型を宣言したり、useStateを使ったりクラスを関数に書き変えたりして置き換えた。

codesandbox: https://codesandbox.io/s/react-autosuggest-typescript-ptn91?fontsize=14&hidenavigation=1&theme=dark

```ReactSuggest.tsx
import React, { BaseSyntheticEvent, FC, useState } from "react";
import Autosuggest from "react-autosuggest";

type LangType = {
  name: string;
  year: number;
};

const languages: LangType[] = [
  {
    name: "CCC",
    year: 1972
  },
  {
    name: "CyberMan",
    year: 1972
  },
  {
    name: "EtherNet",
    year: 2012
  },
  {
    name: "Entertainer",
    year: 10000
  }
];

const getSuggestions = (value: string): LangType[] => {
  const inputValue = value.trim().toLowerCase();
  const inputLength = inputValue.length;

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

const ReactSuggestion: FC = () => {
  const [value, setValue] = useState("");
  const [suggestions, setSuggestions] = useState<LangType[]>([]);

  const getSuggestionValue = (suggestion: LangType): string => {
    const { name } = suggestion;

    return name;
  };

  const renderSuggestion = (suggestion: LangType) => {
    return <div>{suggestion.name}</div>;
  };

  const onChange = (
    event: BaseSyntheticEvent,
    { newValue }: { newValue: string }
  ) => {
    if (event) setValue(newValue);
  };

  const onSuggestionsFetchRequested = ({ value }: { value: string }) => {
    const suggestions: LangType[] = getSuggestions(value);
    setSuggestions(suggestions);
  };

  // Autosuggest will call this function every time you need to clear suggestions.
  const onSuggestionsClearRequested = () => {
    setSuggestions([]);
  };

  const inputProps = {
    placeholder: "cかeを入力してみて",
    value,
    onChange
  };

  return (
    <Autosuggest
      suggestions={suggestions}
      onSuggestionsFetchRequested={onSuggestionsFetchRequested}
      onSuggestionsClearRequested={onSuggestionsClearRequested}
      getSuggestionValue={getSuggestionValue}
      renderSuggestion={renderSuggestion}
      inputProps={inputProps}
    />
  );
};

export default ReactSuggestion;

```

## まとめ

onChange関数の引数に使わないけれど event を入れておかないと怒られた。他にもオプションがあるのでGitHubリポジトリの方で確認してもらえればと思う。
GitHub: https://github.com/moroshko/react-autosuggest#installation