Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is where I'm falling on it. What started as initial excitement over a lens-like addition started to feel more like black magic once I realized that it relies _a lot_ on implicit ordering just to preserve a seemingly simple usage pattern.

While it may _look_ nice to be able to just call `useEffect` and have it infer the rest, it just ends up masking the flow of a hidden parameter into that component, and convolutes the user's understanding of what React is actually doing.



I think people are getting too hung up on the "implicit ordering" thing. The only thing that really matters about the ordering of hooks is that the call pattern remains stable between render passes. This code:

    const [foo, setFoo] = useState("abc");
    const [bar, setBar] = useState("123");
...is functionally the same thing as

    const [bar, setBar] = useState("123");
    const [foo, setFoo] = useState("abc");
The feature is _declarative_ in the same way element rendering is declarative.


Couldn't useState accept a name? How would React DevTools or another debugging tool know the name of a piece of state?


I'm at ReactConf right now and I've been asking everyone this exact question.


Component lifecycle methods are easy to grasp conceptually. At different stages of rendering the component, React will call one of these methods. Easy.

With `useEffect`, I have no idea what is actually happening here, and I've tried re-reading it a few times.


They're easy to grasp conceptually, but they're a pain to use in practice specifically because they force you to split up code which is conceptually related. I've had to deal with some truly monstrous class components in the past which would have been a hell of a lot easier to deal with if we had this.

As for `useEffect`: think about the kind of logic you'd put in `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount`. In CDM, we set up side effects, in CDU, we update those side effects if needed (based on the props, state, and context) and in CWU, we clean up those side effects. If we squint a little, though, we're really only doing two actual operations in those three steps:

didMount: perform side effect A based on props/state/context didUpdate: clean up side effect A, then perform side effect B based on props/state/context didUpdate (again): clean up side effect B, then perform side effect C based on props/state/context willUnmount: clean up side effect C

So the idea here is:

    const [name, setName] = useState("world");

    function modifyTitle() {
      document.title = "Hello, " + name + "!";
      return restoreTitle;
    }

    function restoreTitle() {
      document.title = "Untitled";
    }

    useEffect(modifyTitle);
When this component is rendered, it'll run `modifyTitle` on the first pass, and store its return value (`restoreTitle`) for later. When it's rendered the _next_ time, it'll call `restoreTitle` first and then `modifyTitle` again... which itself returns a new "cleanup function".

So we basically have the same sequence of calls as this is mounted/updated/unmounted:

perform side effect A cleanup side effect A, then perform side effect B cleanup side effect B, then perform side effect C cleanup side effect C

The only difference is we never told React _when_ to perform these steps, just _that it should_ perform them at the appropriate time.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: