0

我正在为食物食谱做即时搜索功能,并希望实现无限滚动组件。我想要的是在开始时显示一个搜索栏,根本没有显示食谱,然后当用户开始输入字母时,它将从 API 获取的数据中获取并显示 8 个项目,当在底部页面向下滚动时它将继续获取并显示接下来的 8 个项目。

我正在按照此链接的说明进行操作,但遇到两个问题:

  • useState如果我按照说明设置初始值,它会显示 8 个空占位符(无数据)

  • 如果我向下滚动,它将获取并显示下一个没有数据的 8 个空占位符

如果我将初始设置useState为空数组[],则可以很好地获取并显示每个占位符以及每个占位符中的数据,所以我对此的理解可能是Array.from(Array(8).keys(), n => n + 1)inuseStateArray.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>
    )
}
4

0 回答 0