diff --git a/optuna_dashboard/ts/components/Debounce.tsx b/optuna_dashboard/ts/components/Debounce.tsx index 31e34739..c8ee1751 100644 --- a/optuna_dashboard/ts/components/Debounce.tsx +++ b/optuna_dashboard/ts/components/Debounce.tsx @@ -1,6 +1,7 @@ import React, { FC, useEffect } from "react" import { TextField, TextFieldProps } from "@mui/material" +// TODO(c-bata): Remove this and use `useDeferredValue` instead. export const DebouncedInputTextField: FC<{ onChange: (s: string, valid: boolean) => void delay: number diff --git a/optuna_dashboard/ts/components/StudyList.tsx b/optuna_dashboard/ts/components/StudyList.tsx index 6c662519..49d5c191 100644 --- a/optuna_dashboard/ts/components/StudyList.tsx +++ b/optuna_dashboard/ts/components/StudyList.tsx @@ -1,4 +1,10 @@ -import React, { FC, useEffect, useState } from "react" +import React, { + FC, + useEffect, + useState, + useDeferredValue, + useMemo, +} from "react" import { useNavigate } from "react-router-dom" import { useRecoilValue } from "recoil" import { Link } from "react-router-dom" @@ -26,7 +32,6 @@ import CompareIcon from "@mui/icons-material/Compare" import DriveFileRenameOutlineIcon from "@mui/icons-material/DriveFileRenameOutline" import { actionCreator } from "../action" -import { DebouncedInputTextField } from "./Debounce" import { studySummariesLoadingState, studySummariesState } from "../state" import { styled } from "@mui/system" import { AppDrawer } from "./AppDrawer" @@ -41,7 +46,8 @@ export const StudyList: FC<{ const theme = useTheme() const action = actionCreator() - const [studyFilterText, setStudyFilterText] = React.useState("") + const [_studyFilterText, setStudyFilterText] = React.useState("") + const studyFilterText = useDeferredValue(_studyFilterText) const studyFilter = (row: StudySummary) => { const keywords = studyFilterText.split(" ") return !keywords.every((k) => { @@ -64,12 +70,14 @@ export const StudyList: FC<{ const query = useQuery() const initialSortBy = query.get("studies_order_by") === "asc" ? "asc" : "desc" const [sortBy, setSortBy] = useState<"asc" | "desc">(initialSortBy) + const filteredStudies = useMemo(() => { + let filteredStudies: StudySummary[] = studies.filter((s) => !studyFilter(s)) + if (sortBy === "desc") { + filteredStudies = filteredStudies.reverse() + } + return filteredStudies + }, [studyFilterText, studies, sortBy]) - let filteredStudies = studies.filter((s) => !studyFilter(s)) - - if (sortBy === "desc") { - filteredStudies = filteredStudies.reverse() - } useEffect(() => { action.updateStudySummaries() }, []) @@ -201,26 +209,23 @@ export const StudyList: FC<{ - { - setStudyFilterText(s) + { + setStudyFilterText(e.target.value) }} - delay={500} - textFieldProps={{ - fullWidth: true, - id: "search-study", - variant: "outlined", - placeholder: "Search study", - sx: { maxWidth: 500 }, - InputProps: { - startAdornment: ( - - - - - - ), - }, + id="search-study" + variant="outlined" + placeholder="Search study" + fullWidth + sx={{ maxWidth: 500 }} + InputProps={{ + startAdornment: ( + + + + + + ), }} /> {sortBySelect} diff --git a/standalone_app/src/components/Debounce.tsx b/standalone_app/src/components/Debounce.tsx deleted file mode 100644 index 31e34739..00000000 --- a/standalone_app/src/components/Debounce.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { FC, useEffect } from "react" -import { TextField, TextFieldProps } from "@mui/material" - -export const DebouncedInputTextField: FC<{ - onChange: (s: string, valid: boolean) => void - delay: number - textFieldProps: TextFieldProps -}> = ({ onChange, delay, textFieldProps }) => { - const [text, setText] = React.useState("") - const [valid, setValidity] = React.useState(true) - useEffect(() => { - const timer = setTimeout(() => { - onChange(text, valid) - }, delay) - return () => { - clearTimeout(timer) - } - }, [text, delay]) - return ( - { - setText(e.target.value) - setValidity(e.target.validity.valid) - }} - {...textFieldProps} - /> - ) -} diff --git a/standalone_app/src/components/StudyList.tsx b/standalone_app/src/components/StudyList.tsx index 4fdd4537..dcad8f27 100644 --- a/standalone_app/src/components/StudyList.tsx +++ b/standalone_app/src/components/StudyList.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from "react" +import React, { FC, useState, useMemo, useDeferredValue } from "react" import { AppBar, Typography, @@ -22,7 +22,6 @@ import Brightness7Icon from "@mui/icons-material/Brightness7" import { useRecoilValue } from "recoil" import { studiesState } from "../state" import { Link } from "react-router-dom" -import { DebouncedInputTextField } from "./Debounce" import { Search } from "@mui/icons-material" import { StorageLoader } from "./StorageLoader" @@ -32,8 +31,9 @@ export const StudyList: FC<{ const theme = useTheme() const studies = useRecoilValue(studiesState) - const [studyFilterText, setStudyFilterText] = useState("") + const [_studyFilterText, setStudyFilterText] = useState("") const [sortBy, setSortBy] = useState<"id-asc" | "id-desc">("id-asc") + const studyFilterText = useDeferredValue(_studyFilterText) const studyFilter = (row: Study): boolean => { const keywords = studyFilterText.split(" ") return !keywords.every((k) => { @@ -43,10 +43,13 @@ export const StudyList: FC<{ return row.study_name.indexOf(k) >= 0 }) } - let filteredStudies: Study[] = studies.filter((s) => !studyFilter(s)) - if (sortBy === "id-desc") { - filteredStudies = filteredStudies.reverse() - } + const filteredStudies = useMemo(() => { + let filteredStudies: Study[] = studies.filter((s) => !studyFilter(s)) + if (sortBy === "id-desc") { + filteredStudies = filteredStudies.reverse() + } + return filteredStudies + }, [studyFilterText, studies, sortBy]) const Select = styled(TextField)(({ theme }) => ({ "& .MuiInputBase-input": { @@ -131,26 +134,23 @@ export const StudyList: FC<{ - { - setStudyFilterText(s) + { + setStudyFilterText(e.target.value) }} - delay={500} - textFieldProps={{ - fullWidth: true, - id: "search-study", - variant: "outlined", - placeholder: "Search study", - sx: { maxWidth: 500 }, - InputProps: { - startAdornment: ( - - - - - - ), - }, + id="search-study" + variant="outlined" + placeholder="Search study" + fullWidth + sx={{ maxWidth: 500 }} + InputProps={{ + startAdornment: ( + + + + + + ), }} /> {sortBySelect}