Eric Jansen
Eric Jansen

Reputation: 440

React how to use state in passed function

I am using React context to pass down state. When my state changes, it also changes in the child Component (console logs shows new value), but when this state is used in a function, it doesnt update there (console.log shows old value).

Do I need to rerender the function? How?

  const {user, userInfo, ref} = useSession(); <-- wrapper for useContext
  console.log(userInfo); <--- correct, updated value

  const haalDataOp = async () => {
    console.log(userInfo.enelogic); <--- old value displaying
    ...
  }

I am using the function haalDataOp from a button (onClick)

As someone already mentioned, I could use useRef, but I dont understand why. Why does this simple example work (extracted from https://dev.to/anpos231/react-hooks-the-closure-hell-71m), and my code doesnt:

  const [value, setValue] = useState(1);

  const handleClick = useCallback(
    () => {
      setValue(value + 1)
    },
    [value],
  );

I also tried using useCallback (with userInfo in the dep array) in my example but that doesnt do the trick.

Upvotes: 0

Views: 132

Answers (1)

Aprillion
Aprillion

Reputation: 22304

const ... userInfo ... is a constant, so in a Component like following:

console.log('render', userInfo.enelogic) // different value in each render
const haalDataOp = async () => {
  console.log('before', userInfo.enelogic) // correct old value
  await update()
  console.log('after', userInfo.enelogic)  // still the same old value
}

return <button onClick={haalDataOp} />

...it would log:

render old
before old
after old
render new

...because the userInfo inside haalDataOp is a closure referencing to the value from the original render. If you need to access a mutable reference that would point to the up-to-date value from a future render instead, you can useRef:

const userInfoRef = useRef()
userInfoRef.current = userInfo

console.log('render', userInfo.enelogic) // different value in each render
const haalDataOp = async () => {
  console.log('before', userInfoRef.current.enelogic) // old value
  await update()
  console.log('after', userInfoRef.current.enelogic)  // should be new value
}

return <button onClick={haalDataOp} />

However, there might be a race condition and/or the execution of the 'after' code happens deterministically BEFORE the next render, in which case you will need to use some other trick...

I suspect that the const {ref} = useSession() is needed for exactly this situation, so please read the documentation.

Upvotes: 1

Related Questions