💭

MUIのAutoCompleteの候補に過去に入力した値を表示する

2025/02/01に公開

検証環境

  • mui v6.3.1
  • react v18.3.1

MUI Autocompleteの基本的な使い方

オプションを配列で渡してあげるとAutoCompleteの候補として表示してくれます。

FormSample.tsx
export const FormSample = (): JSX.Element => {
  const SubmitButton = styled(Button)<ButtonProps>(() => ({
    "&:hover": {
      backgroundColor: [700],
    },
  }));

  return (
    <>
      <Box>
        <Typography variant="h4" textAlign={"center"} mb={"24px"}>
          Form Sample
        </Typography>
        <Autocomplete
          disablePortal
          options={[
            "The Shawshank Redemption",
            "The Godfather",
            "The Dark Knight",
          ]}
          sx={{ width: 300 }}
          renderInput={(params: any) => <TextField {...params} label="Movie" />}
        />
        <SubmitButton
          variant="contained"
          sx={{ marginTop: 4 }}
          onClick={() => {}}
        >
          登録
        </SubmitButton>
      </Box>
    </>
  );
};

参考:MUI Autocomplete

実現したいこと

AutoCompleteの候補に過去に入力したInput内容を表示したい。

方法

LocalStorageを利用し、過去のInput履歴を保存、取得する方法を考えました。

  1. 検索実装ボタンが押下された際にInput内容をLocalStorageにPush
  2. 検索画面を表示し直した際にInputの履歴を取得し、動的にAutoCompleteの候補に渡す

実装

以下のようにコードを修正しました。

  • MovieSearchHistoryというキーを設定。このキーでLocalStorageの内容を取得したり更新したりします
  • useEffectでLocalStorageのMovieSearchHistoryに紐づくデータを取得し、配列に変換してAutocompleteのoptionsに設定
  • 検索ボタン押下時にuseEffectで取得したデータの配列にinputの入力値を追加。同時に重複チェックと件数チェック(ここではLocalStorageに保持する履歴はMax3件としています)を行い、LocalStorageのMovieSearchHistoryを更新する
  • [リファクタリング]データ取得などのロジックとビューを切り分けました。

コンポーネント全体

FormSample.tsx
import {
  Autocomplete,
  Box,
  Button,
  type ButtonProps,
  TextField,
  Typography,
  styled,
} from "@mui/material";
import { useEffect, useState } from "react";

interface FormSampleProps {
  options: string[];
  pushLocalStorage: () => void;
  inputValue: string;
  setInputValue: (value: string) => void;
}

const FormSampleProvider = ({
  options,
  pushLocalStorage,
  setInputValue,
  inputValue,
}: FormSampleProps): JSX.Element => {
  const SubmitButton = styled(Button)<ButtonProps>(() => ({
    "&:hover": {
      backgroundColor: [700],
    },
  }));

  return (
    <>
      <Box>
        <Typography variant="h4" textAlign={"center"} mb={"24px"}>
          Form Sample
        </Typography>
        <Autocomplete
          freeSolo
          sx={{ width: 300 }}
          autoComplete={true}
          options={options}
          inputValue={inputValue}
          onInputChange={(_event, newInputValue) =>
            setInputValue(newInputValue)
          }
          renderInput={(params) => <TextField {...params} label="Movie" />}
        />
        <SubmitButton
          variant="contained"
          sx={{ marginTop: 4 }}
          onClick={() => {
            pushLocalStorage();
          }}
        >
          登録
        </SubmitButton>
      </Box>
    </>
  );
};

export const FormSample = (): JSX.Element => {
  const [inputValue, setInputValue] = useState("");
  const [options, setOptions] = useState<string[]>([]);

  // LocalStorageのキー
  const LOCAL_STORAGE_KEY = "MovieSearchHistory";

  // LocalStorageから履歴を取得
  useEffect(() => {
    const storedHistory = localStorage.getItem(LOCAL_STORAGE_KEY);
    if (storedHistory) {
      setOptions(JSON.parse(storedHistory));
    }
  }, []);

  // 検索実行ボタンのクリック時に履歴を更新
  const pushLocalStorage = () => {
    if (!inputValue.trim()) return;

    const updatedHistory = [...new Set([inputValue, ...options])].slice(0, 3); // 重複を排除し最大3件まで
    setOptions(updatedHistory);
    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(updatedHistory));
  };

  return (
    <FormSampleProvider
      options={options}
      pushLocalStorage={pushLocalStorage}
      inputValue={inputValue}
      setInputValue={setInputValue}
    />
  );
};

動作結果

Discussion