preactjs / preact

⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
https://preactjs.com
MIT License
36.78k stars 1.96k forks source link

Unmount hooks should be called during the commit phase to ensure consistency with React #4299

Open farwayer opened 8 months ago

farwayer commented 8 months ago

Describe the bug

I've run into issues several times that are caused by calling cleanup functions during the rendering phase. First time in the UI library (a similar case is described here https://github.com/preactjs/preact/issues/4222). The second time with a library that prohibits data modification at the rendering stage.

It would be nice for React compatibility and to prevent problems like this to call cleanup functions on hooks after rather than during rendering.

JoviDeCroock commented 7 months ago

They are called as part of the commit phase 😅 mind posting a reproduction or something we can base our discussion off of? Or do you mean deferring the hook cleanups? https://github.com/preactjs/preact/blob/main/hooks/src/index.js#L127

farwayer commented 7 months ago

Yes, you are right. I'm talking about calling the cleanup function of a child component while rendering the parent, if the child component is unmounted. The timing of the call is different from React and this sometimes causes problems.

let Parent = () => {
  console.log('parent render start')
  useLayoutEffect(() => console.log('parent render end'))

  let [showChild, setShowChild] = useState(true)

  useEffect(() => {
    setTimeout(() => setShowChild(false), 1000)
  }, [])

  return showChild ? <Child/> : null
}

let Child = () => {
  useEffect(() => () => console.log('child cleanup'), [])
  return null
}

React:

parent render start
parent render end
parent render start
parent render end
child cleanup

Preact:

parent render start
parent render end
parent render start
child cleanup
parent render end