我正在为食物食谱做即时搜索功能,并希望实现无限滚动组件。我想要的是在开始时显示一个搜索栏,根本没有显示食谱,然后当用户开始输入字母时,它将从 API 获取的数据中获取并显示 8 个项目,当在底部页面向下滚动时它将继续获取并显示接下来的 8 个项目。
我正在按照此链接的说明进行操作,但遇到两个问题:
useState
如果我按照说明设置初始值,它会显示 8 个空占位符(无数据)如果我向下滚动,它将获取并显示下一个没有数据的 8 个空占位符
如果我将初始设置useState
为空数组[]
,则可以很好地获取并显示每个占位符以及每个占位符中的数据,所以我对此的理解可能是Array.from(Array(8).keys(), n => n + 1)
inuseState
和Array.from(Array(8).keys(), n => n + prevState.length + 1)
in的代码与function fetchMoreListItems()
我的情况无关。
我的问题是如何实现正确的代码,以便它可以获取我想要的对象数量的正确数据,所以当我向下滚动时它也可以使用滚动条。谢谢大家!
这是我的演示:gif
这是我的代码:
// 食谱.js
import React, { useState, useEffect } from "react"
import Axios from "axios"
import RecipeCard from "../components/RecipeCard"
import SearchBar from "../components/SearchBar"
import "../style/Recipes.css"
export default function Recipes() {
const [isLoading, setIsLoading] = useState(false)
const [query, setQuery] = useState("")
const [recipes, setRecipes] = useState(Array.from(Array(8).keys(), n => n + 1))
const [isFetching, setIsFetching] = useState(false)
const url = `https://www.themealdb.com/api/json/v1/1/search.php?s=${query}`
// function to search for the recipes when enter `search button`\
const searchRecipes = async () => {
if (query !== "") {
setIsLoading(true)
const result = await Axios.get(url)
console.log(result)
setRecipes(result.data.meals)
setQuery("")
setIsLoading(false)
}
}
// do instant search when start typing any of letters
useEffect(async () => {
if (query !== "") {
const result = await Axios.get(url)
console.log(result);
setRecipes(result.data.meals)
}
}, [query])
// handle handleScroll
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [])
function handleScroll() {
if (window.innerHeight + document.documentElement.scrollTop + 1 >= document.documentElement.offsetHeight) return
setIsFetching(true)
}
useEffect(() => {
if (!isFetching) return
fetchMoreListItems()
}, [isFetching])
function fetchMoreListItems() {
setTimeout(() => {
setRecipes(prevState => ([...prevState, ...Array.from(Array(8).keys(), n => n + prevState.length + 1)]))
setIsFetching(false)
}, 2000);
}
const onChange = async e => {
setQuery(e.target.value)
}
const handleSubmit = e => {
e.preventDefault()
searchRecipes()
}
const clearInput = () => {
setRecipes([])
setQuery("")
}
return (
<div className="recipes">
<div className="search-box">
<h1>Recipe App</h1>
<SearchBar
handleSubmit={handleSubmit}
value={query}
name="name"
onChange={onChange}
isLoading={isLoading}
/>
{
query.length !== 0 &&
<div className="close-icon" onClick={clearInput}>
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 1024 1024" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg>
</div>
}
<div className="search-result">
{
recipes && query !== "" &&
recipes.slice(0, 5).map((val) => {
return (
<a className="search-item" href={val.strSource} target="_blank" rel="noopener noreferrer">
<p>{val.strMeal}</p>
</a>
)
})
}
</div>
</div>
<div className="recipes-container">
{
recipes && query !== null ?
recipes.map((recipe) => (
<RecipeCard
key={recipe.idMeal}
recipe={recipe}
/>
))
: "We're sorry! No recipe found."
}
</div>
{isFetching && 'Fetching more recipes...'}
</div>
)
}