React hooks: get the current state, back to the future

Sébastien Castiel on February 19, 2019

React Hooks are trully awesome, but the more I play with them the more I discover tricks, and sometimes spend a lot of time figuring out why my cod... [Read Full]
markdown guide
 

So this might seem unexpected in this case. But let's consider an equivalent case that looks a bit more concrete than a counter.

handleFollowClick() {
  setTimeout(() => {
    alert('Followed: ' + this.props.user.name);
  }, 5000)
}

Do you see the bug in this code? If the user prop changes before the timeout finishes (e.g. because you navigated to a different profile), you would "follow" the wrong person!

How would you fix it? With a class, one way to fix it is to destructure early:

handleFollowClick() {
  const {user} = this.props;
  setTimeout(() => {
    alert('Followed: ' + user.name);
  }, 5000)
}

Many people wouldn't notice the difference, but this code takes care to remember which user you referred to at the time of the click.

So how does this relate to Hooks? With Hooks, the code behaves like the second snippet by default. You always get the values in the event handlers that were actually rendered.

function handleFollowClick() {
  // There is no "this", the "user" is always
  // the one corresponding to this event handler.
  setTimeout(() => {
    alert('Followed: ' + user.name);
  }, 5000)
}

As you described, a ref is a way to "escape" that snapshot time in time and peek into the future. In many cases you don't want it; but it's there for when you need it. It takes time to adjust to a new default though.

 

Thanks Dan, it kind of confirms (and explains very well) what I suspected 🙂

 
 

Why not just hoist your alert and pass the value at render time?

import { memoize } from 'lodash';

const onAlertButtonClick = memoize((count) => () =>
  setTimeout(() => alert('Value: ' + count), 5000)
);

const Counter = () => {
  const [counter, setCounter] = useState(0)
  const onButtonClick = useCallback(() => setCounter(counter + 1), [counter])

  return (
    <div>
      <p>You clicked {counter} times.</p>
      <button onClick={onButtonClick}>Click me</button>
      <button onClick={onAlertButtonClick(counter)}>
        Show me the value in 5 seconds
      </button>
    </div>
  )
}
 

Great post, Sébastien~

The problem statement & the steps to get the "current" prop (not the closed prop) was fun to read 🙂

In case anyone wants a runnable code, here it is.

 

I have been writing OO code in Java for nearly three decades, I have been writing functional code for about three months now so excuse me if this sounds like I'm just ignorant of a few things. My first question is, isn't it functional programming's goal to not maintain state information between method calls? Second is, if you combine this with TypeScript, don't you now just have plain ol' Java again?

 

Yes, in general functional programming styles tend towards stateless code and immutable data.

React hooks are constructed specifically to access and change stateful information in the context of a functional component. There are ways to solve this issue using stateless functional programming practices, but I don't think it's the react team's goal to enforce any particular programming style. Thus, hooks are a pragmatic solution.

I don't think Typescript and React hooks (or even stateful programming) equates to Java. For example, while Java 8 introduced lambda expressions, lambdas and first class functions have been a part of javascript since its creation. While javascript and typescript have implemented the class keyword, the inheritance structure of javascript is still prototypal, not class based.

Like react, Typescript and javascript are pragmatic in their approach to different coding styles. For example, you can generally approach React from an object oriented standpoint and never write a single functional component. Likewise, you could eschew react hooks and write a completely stateless react application that passes all data out from a single state monad.

Does this help at all?

code of conduct - report abuse