【发布时间】:2021-01-18 23:48:32
【问题描述】:
下面是我的 2 个 js 文件。
我将一个方法传递给子 SearchBox,从其中我从搜索文本字段中检索值并在父级 CourseList.js 中调用该方法。当我取回父级内部的值时,我将该值传递给 fetch api 调用作为从 api 检索数据的查询参数。
我只想在用户停止输入 2 秒和/或当用户单击回车键时从 api 获取数据。现在它只是在输入更改时从 api 获取数据,所以我不想要多次调用 api。
这是我的代码,
课程列表.js
import React, { useState, useEffect, useContext } from "react";
import { Link } from "react-router-dom";
import {
List,
ListItem,
ListItemAvatar,
ListItemText,
Typography,
Box,
Divider,
Grid,
Button,
TablePagination,
} from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import LanguageDropdown from "./LanguageDropdown";
import RatingDropdown from "./RatingDropdown";
import CategoryDropdown from "./CategoryDropdown";
import LevelDropdown from "./LevelDropdown";
import StreamDropdown from "./StreamDropdown";
import SearchBox from "./SearchBox";
import CourseSort from "./CourseSort";
import Pagination from "@material-ui/lab/Pagination";
import settings from "../../../../settings";
const getTruncated = (items, value) => {
// console.log("coming from getTruncated func ", items);
let cut = items.indexOf(" ", value);
if (cut == -1) {
return items;
}
return items.substring(0, cut);
};
export default function CourseList(props) {
const classes = useStyles();
const [myCourses, setMyCourses] = useState([]);
const [page, setPage] = useState(0);
const [cate, setCate] = useState(true);
const [rowsPerPage, setRowsPerPage] = useState(5);
const [search, setSearch] = useState("");
const [userLessons, setUserLessons] = useState([]);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
useEffect(() => {
let mounted = true;
if (window.location.pathname !== "/portal/ondemand") {
setCate(false);
}
setMyCourses(props.userCourses);
getInitSignon();
return () => {
mounted = false;
};
}, [search]);
const getInitSignon = () => {
const token = localStorage.getItem("cars-crm-token");
fetch(settings.api().lessons + `?SearchText=${search}`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((res) => {
if (res.status === 200) {
return res.json();
} else {
const error = new Error(res.error);
throw error;
}
})
.then((data) => {
console.log("coming from onDemandcourse", data);
setUserLessons(data);
})
.catch((err) => {
console.error(err);
});
};
const searchedItem = (value) => {
setSearch(value)
};
return (
<>
<Grid container className={classes.searchsort}>
<Grid item>
<SearchBox
className={classes.searchContainer}
handleSearched={searchedItem}
/>
</Grid>
</Grid>
<Grid className={classes.root}>
<Grid item className={classes.filters}>
<Grid direction="column">
<LanguageDropdown courses={myCourses} />
{cate && <CategoryDropdown />}
</Grid>
</Grid>
<Grid item>
<List className={classes.courses}>
{userLessons
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((lesson) => (
<>
<div key={lesson.lessonId}>
<ListItem
style={{
display: "flex",
alignItems: "flex-start",
justifyContent: "center",
backgroundColor: "#fff",
padding: "16px 15px",
}}
>
<ListItemAvatar
component={Link}
to={`/portal/ondemand/lessons/${lesson.lessonId}`}
>
<Link
to={`/portal/ondemand/lessons/${lesson.lessonId}`}
>
<img
src={lesson.lessonThumbUrl}
className={classes.img}
alt={lesson.lessonTitle}
/>
</Link>
</ListItemAvatar>
<ListItemText
style={{ margin: "0px" }}
primary={
<React.Fragment>
<Typography
component="span"
variant="body2"
className={classes.inline}
color="textPrimary"
>
<Box fontSize={20}>
<Typography
className={classes.courseTitle}
variant="h6"
>
{lesson.lessonTitle}
</Typography>
</Box>
<Grid
container
direction="row"
justify="space-between"
style={{ margin: "12px 0", maxWidth: "50rem" }}
>
<Grid item>
<Box fontStyle="italic">
<Typography variant="h6">
Code: {lesson.courseCode} |
{lesson.lessonCreated
? lesson.lessonCreated.split("T")[0]
: undefined}{" "}
| Course:{" "}
{lesson.courseTitle}
</Typography>
</Box>
</Grid>
</Grid>
</Typography>
</React.Fragment>
}
secondary={
<Box>
<Typography
variant="body1"
style={{ marginBottom: "12px" }}
>
{getTruncated(lesson.lessonDescription, 150) +
"..."}
</Typography>
<Button
className={classes.startLesson}
edge="start"
variant="contained"
href={`/portal/ondemand/lessons/${lesson.lessonId}`}
>
Start Lesson
</Button>
</Box>
}
/>
</ListItem>
<Divider className={classes.divider} />
</div>
</>
))}
</List>
<TablePagination
rowsPerPageOptions={[10, 50, 100]}
component="div"
count={userLessons.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Grid>
</Grid>
</>
);
}
搜索框.js
import React from "react";
import { TextField } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
TextField: {
width: "100%",
},
menu: {
width: 200,
},
}));
export default function SearchBox(props) {
const handlechangeSearch = (event) => {
props.handleSearched(event.target.value);
};
const classes = useStyles();
return (
<React.Fragment>
<TextField
id="outlined-search"
label="Search"
className={classes.TextField}
onChange={handlechangeSearch}
margin="dense"
variant="outlined"
/>
</React.Fragment>
);
}
需要问题的解决方案,我将不胜感激。提前谢谢!
【问题讨论】:
-
我在父组件中添加了另外 2 个函数,1 个用于 keydown,1 个用于使用 settimeout 进行更改。现在我的 api 调用正在等待 2 秒。但是,按回车键有效,但 2 秒后再次调用 api,并使用默认结果重新渲染它并返回输入的结果。 ```
-
您需要对 api 调用进行去抖动处理。 Loadash 库具有您可以使用的去抖动实用程序,或者您可以自己编写。你甚至可以编写自己的 useDebounce 钩子。该视频展示了如何制作 useDebounce 钩子,它非常有见地,因为它向您展示了在对异步 api 调用进行去抖动时可能遇到的一些问题:youtube.com/watch?v=EXpLFRGM8kg