简体   繁体   English

ReactJS:React setState/useState 没有立即更新

[英]ReactJS: React setState/useState does not update immediately

I'm building a Food Recipe app, I have an instant auto filter search result that query and get data when the user types any of letter.我正在构建一个 Food Recipe 应用程序,我有一个即时自动筛选搜索结果,当用户键入任何字母时查询并获取数据。 For example, with letter "c" it will show list of results which have letter "c" in the ingredients.例如,对于字母“c”,它将显示成分中包含字母“c”的结果列表。 Everything seems to work fine, except the auto filter is delayed one letter backward, so it only show results that contain letter "c" when user type "ch" or "c + one random letter ".一切似乎都工作正常,除了自动过滤器向后延迟一个字母,所以当用户键入“ch”或“c +一个随机字母”时它只显示包含字母“c”的结果。

I read this link: https://linguinecode.com/post/why-react-setstate-usestate-does-not-update-immediatelyand that helps me define why React useState does not update immediately, and I think the solution is to used React useEffect.我阅读了这个链接: https://linguinecode.com/post/why-react-setstate-usestate-does-not-update-immediately这帮助我定义了为什么 React useState 没有立即更新,我认为解决方案是使用反应使用效果。 I'm new in learning React so I have trouble to refactor my code into using useEffect in my case.我是学习 React 的新手,所以我很难将我的代码重构为在我的案例中使用 useEffect。 I would appreciate if someone enlighten me.如果有人启发我,我将不胜感激。 Thank you everyone!谢谢大家!

Here's my demo : gif这是我的演示gif

Here's my code :这是我的代码

// Recipes.js // 食谱.js

export default function Recipes() {

  const [query, setQuery] = useState("")
  const [recipes, setRecipes] = useState([])
  const [alert, setAlert] = useState("")

  const APP_ID = "5b1b4741"
  const APP_KEY = "0325f7a61ac15027151b8060740d90f0"

  const url = `https://api.edamam.com/search?q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`    

  const getData = async () => {
    if (query !== "") {
      const result = await Axios.get(url)
      if (!result.data.more) {
        return setAlert("No food with such name")
      }
      console.log(result)
      setRecipes(result.data.hits)
      setQuery("")
      setAlert("")
    } else {
      setAlert("Please fill the form")
    }
  }
    
  const onChange = async e => {
    setQuery(e.target.value)
    if (query !== "") {
      const result = await Axios.get(url)
      console.log(result)
      setRecipes(result.data.hits)
    }
  }

  const onSubmit = e => {
    e.preventDefault()
    getData()
  };

  return (
    <div className="recipes">
      <div className="search-box">
        <h1>Your Recipe App</h1>

        <form onSubmit={onSubmit} className="search-form">
          {alert !== "" && <Alert alert={alert} />}

          <input
            type="text"
            name="query"
            onChange={onChange}
            value={query}
            placeholder="Search Food"
          />
          <input type="submit" value="Search" />
        </form>
        
        {query.length !== 0 && 
          <div className="search-result">
            {recipes.slice(0, 5).map((val) => {
              return (
                <a className="search-item" href={val.recipe.url} target="_blank" rel="noopener noreferrer">
                  <p>{val.recipe.label}</p>
                </a>
              ) 
            })}
          </div>
        }
      </div>

      <div className="recipes-card">
        {recipes !== [] &&
          recipes.map(recipe => <Recipe key={uuidv4()} recipe={recipe} />)}
      </div>
    </div>

// Recipe.js // 食谱.js

const Recipe = ({ recipe }) => {
  const [show, setShow] = useState(false);
  const { label, image, url, ingredients } = recipe.recipe;

  return (
    <div className="recipe">
      <h2>{label}</h2>
      <img src={image} alt={label} />
      <a href={url} target="_blank" rel="noopener noreferrer">
        Go To Link
      </a>
      <button onClick={() => setShow(!show)}>Ingredients</button>
      {show && <RecipeDetails ingredients={ingredients} />}
    </div>
  )
}

export default Recipe
 const onChange = async e => { setQuery(e.target.value) if (query.== "") { const result = await Axios.get(url) console.log(result) setRecipes(result.data.hits) } }

You're sending the message to set a new value of query but then immediately using the old value (which the function has closed over) to make your Ajax request.您正在发送消息以设置新的query值,但随后立即使用旧值(function 已关闭)来发出您的 Ajax 请求。

Either:任何一个:

  • Just use e.target.value instead of query (don't forget to move the logic that calculates the value of url too).只需使用e.target.value而不是query (不要忘记移动计算url值的逻辑)。
  • Set up a useEffect hook with a dependency on query which has the code from lines 3–7 of what I quoted.设置一个useEffect钩子,它依赖于query ,其中包含我引用的第 3-7 行的代码。

You should use an effect to run every time the query value has changed每次query值发生更改时,您都应该使用效果来运行

useEffect(() => {
  if (query !== "") {
      const result = await Axios.get(url)
      setRecipes(result.data.hits)
  }
}, [query])

const onChange = e => {
  setQuery(e.target.value)
}

You are not forced to use useEffect :您不会被迫使用useEffect

export default function Recipes() {

  const [query, setQuery] = useState("")
  const [recipes, setRecipes] = useState([])
  const [alert, setAlert] = useState("")

  const APP_ID = "5b1b4741"
  const APP_KEY = "0325f7a61ac15027151b8060740d90f0"

  const baseUrl = `https://api.edamam.com/search`    

  const getData = async () => {
    if (query !== "") {
      const result = await Axios.get(baseUrl + `?q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`)
      if (!result.data.more) {
        return setAlert("No food with such name")
      }
      console.log(result)
      setRecipes(result.data.hits)
      setQuery("")
      setAlert("")
    } else {
      setAlert("Please fill the form")
    }
  }
    
  const onChange = async e => {
    setQuery(e.target.value)
    if (query !== "") {
      const result = await Axios.get(baseUrl + `?q=${query}&app_id=${APP_ID}&app_key=${APP_KEY}`)
      console.log(result)
      setRecipes(result.data.hits)
    }
  }

  const onSubmit = e => {
    e.preventDefault()
    getData()
  };

  return (
    <div className="recipes">
      <div className="search-box">
        <h1>Your Recipe App</h1>

        <form onSubmit={onSubmit} className="search-form">
          {alert !== "" && <Alert alert={alert} />}

          <input
            type="text"
            name="query"
            onChange={onChange}
            value={query}
            placeholder="Search Food"
          />
          <input type="submit" value="Search" />
        </form>
        
        {query.length !== 0 && 
          <div className="search-result">
            {recipes.slice(0, 5).map((val) => {
              return (
                <a className="search-item" href={val.recipe.url} target="_blank" rel="noopener noreferrer">
                  <p>{val.recipe.label}</p>
                </a>
              ) 
            })}
          </div>
        }
      </div>

      <div className="recipes-card">
        {recipes !== [] &&
          recipes.map(recipe => <Recipe key={uuidv4()} recipe={recipe} />)}
      </div>
    </div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM