Will ReactDOM.hydrate() trigger lifecycle methods on the client?
Asked Answered
P

3

9

From the React 16 docs about ReactDOM.hydrate(),

Same as render(), but is used to hydrate a container whose HTML contents were rendered by ReactDOMServer. React will attempt to attach event listeners to the existing markup.

  1. Will ReactDOM.hydrate() also trigger lifecycle methods on the client such as componentWillMount(), componentDidMount() during initial render?

  2. Will render() method be called on the client during hydration? I suppose not, because that's the difference between ReactDOM.render() and ReactDOM.hydrate()?

If render method won't be called on the client, we wouldn't expect componentDidMount() lifecycle method to be triggered.

If none of the lifecycle methods are called on the client, how would we know when has React finished rendering. I suppose the callback in the following syntax:

ReactDOM.hydrate(element, container[, callback])

I want to understand if there are lifecycle methods / hooks (which give more control over the application) available when React is "attempting to attach event listeners to existing markup".

Plaintive answered 9/11, 2017 at 13:9 Comment(0)
T
15
  1. Since ReactDOM.hydrate is (and should be) called on the client then YES it is supposed to run componentDidMount. componentWillMount is already called when rendered on the server. componentDidMount does not run on the server, therefore when you call hydrate, the app runs the event.

  2. Think about hydrate as a different render method. It does render but not in the same way. It looks for mismatches between your server rendered React and your client React. It does not render everything again.

React expects that the rendered content is identical between the server and the client. It can patch up differences in text content (such as timestamps), but you should treat mismatches as bugs and fix them

However you might want to do some crazy stuff like rendering something completely different on the client side (than what was rendered on the server). For this pay attention to this paragraph

If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a state variable like this.state.isClient, which you can set to true in componentDidMount(). This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration. Note that this approach will make your components slower because they have to render twice, so use it with caution.

So as you can see it does a render pass. If there are no mismatches React is optimized for that.

I hope it was clarifying. I speak from experience with React SSR and basic understanding of reading the docs.

Treblinka answered 13/11, 2017 at 13:53 Comment(3)
seems like componentWillMount is called both on the server and on the client after calling hydrate. I'm trying to find a way to differentiate between an element being mounted and hydrated and having trouble. ideas welcome!Coldblooded
@AndreyFedorov depends on the use case at hand. What are you trying to do?Adrial
@Andrey, maybe use a useEffect, if it's mounted, then the effects should be called.Darcidarcia
D
1

The rendered elements probably aren't same between server and client, because initially the elements are rendered into texts at the server in memory, therefore they are not mounted. When the content is moved to client, it can be re-attached to react via hydrate which is fake "render" to wire with the rest of react functionalities, such as events.

In order to tell when it's hydated, here's a piece from internet which I found clearly stated the above rational. https://dev.to/merri/understanding-react-ssr-spa-hydration-1hcf?signin=true

const HydrateContext = createContext('hydrated')

export function useIsHydrated() {
    return useContext(HydrateContext)
}

export function IsHydratedProvider({ children }) {
    const [isHydrated, setIsHydrated] = useState(false)
    useEffect(() => {
        setIsHydrated(true)
    }, [])
    return (
        <HydrateContext.Provider value={isHydrated}>
            {children}
        </HydrateContext.Provider>
    )
}

To use it,

function MyComponent() {
    const isHydrated = useIsHydrated()
    return !isHydrated ? 'Initial render' : 'SPA mode'
}

function App() {
    return (
        <IsHydratedProvider>
            <MyComponent />
        </IsHydratedProvider>
    )
}

It feels to me, any rendered component teleports from the server to the client.

p.s Here's another article which talks about the second render after the mount, https://medium.com/swlh/how-to-use-useeffect-on-server-side-654932c51b13

Darcidarcia answered 21/5, 2021 at 18:27 Comment(1)
This solution assumes that effects are always running after hydration is completed. I'm not sure if this assumption is correct though (see this bug report).Autobiographical
A
0

I read the type of ReactDOM.hydrate in TypeScript system:

(
  element: SFCElement<any> | Array<SFCElement<any>>,
  container: Container| null,
  callback?: () => void
): void;

And example to the above declaration:

ReactDOM.hydrate(
  <App />, // element
  document.getElementById('root'), // container
  () => { // callback

    /* do what you want after hydration */
  }
);
Antihistamine answered 13/10, 2021 at 9:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.