Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
197 views
in Technique[技术] by (71.8m points)

reactjs - Why is my state only updated on the second state change instead of first state change with useEffect in React?

I made a search bar that allows the user to search all sports available in one specific city (if sport is not defined) or a specific sport in a specific city (if sport is defined). City will allways be defined.

I put 2 inputs (city and sport) on my searchbar and I want immediate results (so that there is a request to my API without any button "search" that triggers the request).

So when the user types something on the city input it triggers a request to the API and when he types something on the sport input it retriggers the request but this time with the city and the sport defined. Both inputs values are store in states (city and sport).

I manage to do something that seems to work, the only problem is that if I types a sport in my input search, it does not update my request to the API. I have to retype the sport in my input a second time so that the request is updated. I don't know why it does not update the first time I types something in my sport input because I have specified on my useEffect array that it must re render when the sport state changes.

Can someone help me understand this ?

My code :

import React, { useState, useEffect } from "react";
import style from "../styles/pdrs.module.css";
import axios from "axios";


import SearchBar from "../components/SearchBar";

const Pdrs = ({ setSearchCity, searchSport, setSearchSport }) => {
  // if request's result is loading
  const [isLoading, setIsLoading] = useState(false);
  // search result
  const [searchresults, setSearchresults] = useState(
    "Lancez une recherche avec au moins une ville !"
  );
  // state for the searchbar request
  const [city, setCity] = useState("");
  const [sport, setSport] = useState(0);

  // get city's id for API's request
  const fetchCity = async () => {
    setIsLoading(true);
    try {
      // city search
      const cityResponse = await axios.get(
        `${baseAPI}/city/name=${searchCity}`
      );
      const city = cityResponse.data;
      setCity(city);
      setIsLoading(false);
    } catch (error) {
      console.log(error.message);
      setIsLoading(false);
    }
  };

  //fetching sport id
  const fetchSport = async () => {
    setIsLoading(true);
    try {
      const sportResponse = await axios.get(
        `${baseAPI}/activity/name=${searchSport}`
      );
      setSport(sportResponse.data.data[0].macro_activity_id);
      setIsLoading(false);
    } catch (error) {
      console.log(error.message);
    }
  };

  //fetching final request response
  const fetchDataRequest = async () => {
    try {
      setIsLoading(true);
      const results = await axios.get(
        `${baseAPI}/pdrs?city_id=${city.id}${
          sport ? "&macro_activity_id=" + sport : ""
        }`
      );
      // manage search results
      if (results.data.nb_results === 1) {
        setSearchresults({
          data: [results.data.data],
          nb_results: 1,
        });
        setNbResults(1);
        setIsLoading(false);
      } else {
        setSearchresults(results.data);
        setNbResults(results.data.nb_results);
        setIsLoading(false);
      }
    } catch (error) {
      console.log(error.message);
      setSearchresults(
        "Sorry, nothing was found... !"
      );
    }
  };

  useEffect(() => {
    if (searchCity) {
      fetchCity();
    }
    if (searchSport) {
      fetchSport();
    }
  }, [searchCity, searchSport]);

  useEffect(() => {
    if (searchCity) {
      fetchDataRequest();
    }
  }, [searchCity, searchSport]);
  console.log(searchresults);
  return <>
    <main className={`container ${style.pdrs}`}>
      <section className={style.searchbar}>
        <SearchBar
          searchCity={searchCity}
          setSearchCity={setSearchCity}
          searchSport={searchSport}
          setSearchSport={setSearchSport}
          searchInstallation={searchInstallation}
          setSearchInstallation={setSearchInstallation}
          searchType={searchType}
          setSearchType={setSearchType}
          setPage={setPage}
        />
      </section>
      <section className={style.results}>
        {isLoading ? (
          <div>Loading...</div>
        ) : typeof searchresults === "string" ? (
          <div className={`${style.container} ${style.noResults}`}>
            <h2>{searchresults}</h2>
          </div>
        ) : (
          <>
            <div className={style.container}>
              <div className={style.resultsList}>
                {searchresults.data.map((pdrs) => {
                  return (
                 // some code displaying the result
                  );
                })}
              </div>
            </div>
          </>
        )}
      </section>
    </main>
</>;
};

export default Pdrs;

question from:https://stackoverflow.com/questions/65846257/why-is-my-state-only-updated-on-the-second-state-change-instead-of-first-state-c

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Since you are having two useEffect and one is setting city and sport you would need to make debounce for making a call for fetching list by itself.

I would suggest that you firstly make changes to your use effect for API call fetchDataRequest:

useEffect(() => {
  if (searchCity) {
    fetchDataRequest();
  }
}, [city, sport]);

You would listen to the actual data from BE, not from input that you fill.

And secondly you can use library useDebounce from here https://www.npmjs.com/package/use-debounce and use useDebounceCallback to delay calling API call after you select sport/city.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...