dmigo
dmigo

Reputation: 3029

React hooks callback receives outdated state

Trying out react hooks on a simple search component. The idea is simple: user types symbols, every typed symbol initiates api query.
To achieve that I have useState and useCallback hooks like in the code below:


const Search = () => {
  const [query, setQuery] = useState("");
  const sendRequest = useCallback(() => {
    console.log('sendRequest ', query);
  }, [query]);

  return (
    <div>
      <input
        type="text"
        value={query}
        placeholder="Search"
        onChange={e => {
          console.log('onChange ', e.target.value);
          setQuery(e.target.value);
          sendRequest();
        }}
      />
    </div>
}

The result is that sendRequest method always gets a previous version of query.

onChange q
sendRequest 
onChange qu
sendRequest q
onChange que
sendRequest qu

Why is that? I assume that this is not how the hooks are supposed to be used, but I can't figure that out from the documentation.

Upvotes: 0

Views: 356

Answers (3)

Domino987
Domino987

Reputation: 8774

setState is asynchronous! At the time you send sendRequest, the local state is not updated, because it is asynchronous and it needs some time to get set.

You should either give the string as a parameter into the function or useEffect and listen to changes of query.

Exchanging useCallback with useEffect and removing the call in onChange should work.

const Search = () => {
  const [query, setQuery] = useState("");

  useEffect(() => {
    console.log('sendRequest ', query);
  }, [query]);

  return (
    <div>
      <input
        type="text"
        value={query}
        placeholder="Search"
        onChange={e => {
          setQuery(e.target.value);
        }}
      />
    </div>
}

Upvotes: 1

walid sahli
walid sahli

Reputation: 416

hey bro you can try this implementation its works as you expect

const [query, setQuery] = useState("");
  const sendRequest = e => {
    setQuery(e);
    console.log('sendRequest ', e);
  };

  return (
    <div>
      <input
        type="text"
        value={query}
        placeholder="Search"
        onChange={e => {
          console.log('onChange ', e.target.value);
          sendRequest(e.target.value);
        }}
      />
    </div>)

Upvotes: 1

tolotra
tolotra

Reputation: 3270

Use useEffect instead useCallback. useEffect fires your callback function when query changes.

useEffect(() => { console.log(query) }, [query])

Upvotes: 1

Related Questions