React JS中如何按条件调用两个API实现默认热门展示与搜索查询功能
问题修复方案
核心错误点
- API调用逻辑写反:现有代码有搜索关键词时请求热门接口,无搜索词时请求搜索接口,与需求完全相反
- 过滤函数逻辑颠倒:有搜索词时未做匹配直接返回所有数据,无搜索词时反而执行关键词匹配,逻辑完全错误
- 无结果判断逻辑错误:仅判断原始接口返回数组长度,过滤后的数组为空时不会触发提示,导致出现空白页面
- 搜索接口使用HTTP协议:会被浏览器混合内容安全策略拦截,导致请求失败无返回
- 缺失字段兜底:热门列表可能包含剧集类内容,无
original_title字段,会出现空标题卡片
修正后完整代码
import React ,{useEffect, useState} from 'react'; import {Grid, Button, Card, CardActionArea, CardActions, CardContent, CardMedia, Typography, makeStyles } from '@material-ui/core'; import Rating from '@material-ui/lab/Rating'; import Watchlist from '../Products/Watchlist.jsx'; import Error from '../Error/Error.jsx'; const useStyles = makeStyles (theme => ({ cardButton:{ '&:hover':{ backgroundColor:'#356E44', color: 'white', } }, Main:{ width: "100vw", justifyContent: " flex-start", display: "flex", flexWrap: "wrap", alignItems: "stretch", alignContent: " flex-start", columnGap: "20px", rowGap:"5px", }, cardMain:{ width: 'min-content', height: 'min-content', margin: '10px', '&:hover':{ boxShadow: '5px 3px 5px white', } }, cardImage:{ height: '150px', marginLeft: '7px', marginRight: '7px', marginTop: '5px', objectFit: 'cover', }, cardContent: { width: '250px', height: '50px', wordBreak: 'break-word', fontSize: '1rem', fontWeight: 'bolder', }, typography1:{ float: 'right', }, typography2:{ float: 'left', }, })); const Products = (props) =>{ const classes = useStyles(); const [movieData, setMovieData] = useState([]); const [watchlist, setWatchlist] = useState ([]); // 修正过滤逻辑 const filterData = (val) => { const keyword = props.searchText?.trim().toLowerCase() || ''; // 无搜索词直接返回所有数据 if (!keyword) return true; // 有搜索词时做匹配,增加字段兜底 const title = (val.original_title || val.original_name || '').toLowerCase(); return title.includes(keyword); }; useEffect (()=> { const getMovieList = async () => { const TrendingUrl = 'https://api.themoviedb.org/3/trending/all/day?api_key=API_keyID' ; // 修正为HTTPS协议,关键词做编码处理 const allResultsUrl = `https://api.themoviedb.org/3/search/movie?api_key=API_keyID&query=${encodeURIComponent(props.searchText)}`; // 修正API调用逻辑:有搜索词调用搜索接口,无则调用热门接口 const targetUrl = props.searchText?.trim() ? allResultsUrl : TrendingUrl; const response = await fetch(targetUrl); try { const responseJson = await response.json(); const data = responseJson.results || []; setMovieData(data); } catch (err) { console.error(err); setMovieData([]); } }; getMovieList(); }, [props.searchText]); const handleWatchlist = (movieItem) => { const newWatchlist = [...watchlist , movieItem]; setWatchlist (newWatchlist); } // 先拿到过滤后的数据再做判断 const filteredList = movieData.filter(filterData); return ( <> <Grid item xs container direction="column" justifyContent="flex-start" alignItems="flex-start" style={{ backgroundColor: "black", width: "100%", height: "100%"}} > <div className ={classes.Main} > {/* 判断过滤后的数组长度,而非原数组 */} { filteredList.length === 0 ? ( <h1 style = {{color: 'red', fontSize : "2rem"}}>No search results found</h1> ) : filteredList.map((movie) =>{ return( <Card className={classes.cardMain} key={movie.id}> <CardActionArea> <CardMedia className = {classes.cardImage}> <img style = {{width: '100%', height: '100%', objectFit: 'cover'}} src ={movie.poster_path ? `https://image.tmdb.org/t/p/original${movie.poster_path}` : 'https://via.placeholder.com/250x150?text=No+Image'} alt = "movie poster"/> </CardMedia> <CardContent className = {classes.cardContent}> <Typography> {movie.original_title || movie.original_name || '无标题'} </Typography> <Typography className = {classes.typography1} variant="body2" component = "p" > {movie.release_date || movie.first_air_date || '暂无日期'} </Typography> <Rating className = {classes.typography2} name = "ratings" value = {movie.vote_average ? movie.vote_average/2 : 0} precision={0.5} readOnly /> </CardContent> </CardActionArea> <CardActions style = {{justifyContent: 'space-evenly'}} > <Button className = {classes.cardButton} size = "small">Watch</Button> <Button className = {classes.cardButton} size = "small" >Share</Button> <Button className = {classes.cardButton} size = "small" onClick = { () => handleWatchlist(movie) }> Add </Button> </CardActions> </Card> ); })} </div> </Grid> </> ) }; export default Products;
可选优化点
- 增加请求loading状态,请求过程中展示加载提示,提升用户体验
- 增加防抖逻辑,避免搜索输入过程中频繁触发接口请求
内容的提问来源于stack exchange,提问作者Tapesh Patel
相关产品推荐
相关产品推荐

