初めに
タイトルが説明的でかつ長い、、
debounceを使ったAutocompleteがあまりにもひどかったので、きちんとそのあたりも含めての見直し記事
前回アップしているのは削除済み
こんな感じのもの
※画面側の動作確認のために、サーバー側はFastAPI側でGoogleに候補検索をかけにいっています
コード
App.js
import { useState, useEffect } from "react";
import { TextField, Autocomplete, Stack } from "@mui/material";
import { debounce } from "lodash";
import axios from "axios";
const App = () => {
// 前回のキーワード
const [beforeString, setBeforeString] = useState("");
// 表示候補(本来はreduxのuseSelectorなんかでデータを管理する)
const [resultOptions, setResultOptions] = useState([]);
// 選択されている内容
const [autocompleteValues, setAutocompleteValues] = useState([]);
// リクエストするURL
const baseUrl = "http://localhost:8000/api/search/candidate/words/";
// 変更が止まった後、指定時間(ミリ秒)後処理を行う
const term = 500;
const debounceSearchHandler = debounce(
(changeValue) => searchMethod(changeValue),
term
);
// 検索処理
const searchMethod = async (changeValue) => {
setBeforeString(changeValue);
// 内容が同じ場合は処理しない
if (changeValue.length > 0 && beforeString !== changeValue) {
// FetchしてAutocompleteのoptionsの内容を取得する処理
// [{title: keyword1}, {title: keyword2}, ...] が返ってくることを想定
const responce = await axios.get(`${baseUrl}${changeValue}`);
setResultOptions([...responce.data]);
} else {
// オートコンプリートの内容初期化
setResultOptions([]);
}
};
// 選択データが変わったときの処理
const selectedChange = (event, selectedValue) => {
// 選択されているオートコンプリート内容変更
setAutocompleteValues([...selectedValue]);
// オートコンプリートの内容初期化
setResultOptions([]);
};
// 選択データの内容確認
useEffect(() => {
console.log("autocompleteValues", autocompleteValues);
}, [autocompleteValues]);
return (
<Stack sx={{ width: 500 }}>
<Autocomplete
multiple // 複数選択
freeSolo // 開閉をなくす
options={resultOptions}
onInputChange={(e, changeValue) => {
debounceSearchHandler(changeValue);
}} // テキスト内容変更時
onChange={selectedChange} // 選択状態変更時
renderInput={(params) => (
<TextField
{...params}
placeholder="候補入力"
InputProps={{
...params.InputProps,
type: "search",
}} // 全角入力時のオートコンプリートを出さない対策
/>
)}
filterOptions={(options) => options} // オートコンプリートの検索処理をなくす
getOptionLabel={(option) => option.title}
/>
</Stack>
);
};
export default App;
テストのサーバー側
※FastAPIで作成
main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import json
import uvicorn
import os
from candidate import requestWords
app = FastAPI()
# クロス設定
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api/search/candidate/words/{input}")
def read_item(input: str):
keyWords = requestWords.getCandidateWords(input)
keyMap = []
for word in keyWords:
addKeywordMap = {"title": word}
keyMap.append(addKeywordMap)
return keyMap
if __name__ == "__main__":
uvicorn.run(app)
candidate/requestWords.py
import pprint
import requests
from lxml import etree
import numpy as np
import pandas as pd
def getCandidateWords(specWord: str):
print(specWord)
# 単語格納用
google_suglist = []
# オートコンプリートのデータを取得する
google_r = requests.get(
"https://www.google.com/complete/search",
params={
"q": specWord,
"hl": "ja",
"ie": "utf_8",
"oe": "utf_8",
"output": "toolbar",
},
)
google_root = etree.XML(google_r.text)
google_sugs = google_root.xpath("//suggestion")
google_sugstrs = [s.get("data") for s in google_sugs]
for ss in google_sugstrs:
google_suglist.append(ss)
return google_suglist
終わりに
とりあえず、動作確認大事