简体   繁体   中英

React not setting hook state despite using hook setter

I have a search bar input:

      <input
          type="text"
          className="px-4 py-2 w-80"
          placeholder="Search"
          onChange={(event) => searchWord(event.target.value)}
        />

I have this set up at the top of my file:

  const [foundWords, setFoundWords] = useState<any>([]);
  const [searchString, setSearchString] = useState('');

  const fetchWords = async () => {
    console.log("1", searchString);
    axios.get(`http://localhost:8081/find?searchString=${searchString.toLowerCase()}`)
      .then(({ data }) => setFoundWords(data));
  }

  const debounce = (fn: Function, ms = 300) => {
    let timeoutId: ReturnType<typeof setTimeout>;
    return function (this: any, ...args: any[]) {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => fn.apply(this, args), ms);
    };
  };

  const searchWord = debounce((input: string) => {
    console.log("2", input)
    setSearchString(input);
    if (!input) return;
    fetchWords()
  }, 500);

The console log "1" above returns an empty string, but the console log "2" returns my search query.

What's going wrong with it?

You can use an useEffect instead of directly calling the fetchWords from the searchWord function

I would update your code the following way

  const [foundWords, setFoundWords] = useState<any>([]);
  const [searchString, setSearchString] = useState('');

  const fetchWords = useCallback((input: string) => {
    axios.get(`http://localhost:8081/find?searchString=${input.toLowerCase()}`)
      .then(({ data }) => setFoundWords(data));
  }, [])

  const debounce = (fn: Function, ms = 300) => {
    let timeoutId: ReturnType<typeof setTimeout>;
    return function (this: any, ...args: any[]) {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => fn.apply(this, args), ms);
    };
  };

  const searchWord = debounce((input: string) => {
    setSearchString(input);
  }, 500)

  useEffect(() => {
    if (searchString) {
      fetchWords(searchString)
    }
  }, [searchString, fetchWords])

UseEffect would be guaranteed to trigger only after the searchString is updated. And as per useCallback for fetchWords . It is a better optimization and a good practice for functions used inside useEffect to be wrapped inside useCallback

Also, you can further optimize your code in the following way if it is not required to trigger render cycle again on change of searchString .

  const [foundWords, setFoundWords] = useState<any>([]);

  const fetchWords = useCallback((input: string) => {
    axios.get(`http://localhost:8081/find?searchString=${input.toLowerCase()}`)
      .then(({ data }) => setFoundWords(data));
  }, [])

  const debounce = (fn: Function, ms = 300) => {
    let timeoutId: ReturnType<typeof setTimeout>;
    return function (this: any, ...args: any[]) {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => fn.apply(this, args), ms);
    };
  };

  const searchWord = debounce((input: string) => {
    if (input) {
      fetchWords(input)
    }
  }, 500)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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