From 67c3c20db0abc7ef1f54f237944f680bdb2e9a90 Mon Sep 17 00:00:00 2001 From: Sercan AKMAN Date: Thu, 20 Apr 2023 09:32:02 +0300 Subject: [PATCH] docs(core): removing the newly added explanations for `useIsBrowser` as they are not in the scope of only Docusaurus but all the React SSR frameworks and extending the existing example to include a bit more context --- website/docs/docusaurus-core.mdx | 77 +++++++------------------------- 1 file changed, 17 insertions(+), 60 deletions(-) diff --git a/website/docs/docusaurus-core.mdx b/website/docs/docusaurus-core.mdx index 251f7d0fc3..f7847cace2 100644 --- a/website/docs/docusaurus-core.mdx +++ b/website/docs/docusaurus-core.mdx @@ -413,7 +413,7 @@ Returns `true` when the React app has successfully hydrated in the browser. :::caution -Use this hook instead of `typeof windows !== 'undefined'` in React rendering logic. +Use this hook instead of `typeof windows !== 'undefined'` in React rendering logic because `window` may be defined but hydration may not necessarily have been completed yet. The first client-side render output (in the browser) **must be exactly the same** as the server-side render output (Node.js). Not following this rule can lead to unexpected hydration behaviors, as described in [The Perils of Rehydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/). @@ -427,70 +427,27 @@ import useIsBrowser from '@docusaurus/useIsBrowser'; const MyComponent = () => { // highlight-start + // Recommended const isBrowser = useIsBrowser(); + + // Not Recommended + // using typeof window !== 'undefined' will lead to mismatching render output + const isWindowDefined = typeof window !== 'undefined'; // highlight-end - return
{isBrowser ? 'Client' : 'Server'}
; + return ( +
+ {/* Recommended */} + {isBrowser ? 'Client (hydration completed)' : 'Server'} + + {/* Not Recommended */} + {isWindowDefined + ? 'Client (hydration NOT completed, will mismatch)' + : 'Server'} +
+ ); }; ``` -#### A caveat to know when using `useIsBrowser` - -Because it does not do `typeof windows !== 'undefined'` check but rather checks if the React app has successfully hydrated, the following code will not work as intended: - -```jsx -import React from 'react'; -import useIsBrowser from '@docusaurus/useIsBrowser'; - -const MyComponent = () => { - // highlight-start - const isBrowser = useIsBrowser(); - const url = isBrowser ? new URL(window.location.href) : undefined; - const someQueryParam = url?.searchParams.get('someParam'); - const [someParam, setSomeParam] = useState(someQueryParam || 'fallbackValue'); - - // renders fallbackValue instead of the value of someParam query parameter - // because the component has already rendered but hydration has not completed - // useState references the fallbackValue - return {someParam}; - // highlight-end -}; -``` - -Adding `useIsBrowser()` checks to derived values will have no effect. Wrapping the `` with `` will also have no effect. To have `useState` reference the correct value, which is the value of the `someParam` query parameter, `MyComponent`'s first render should actually happen after `useIsBrowser` returns true. Because you cannot have if statements inside the component before any hooks, you need to resort to doing `useIsBrowser()` in the parent component as such: - -```jsx -import React, {useState} from 'react'; -import useIsBrowser from '@docusaurus/useIsBrowser'; - -const MyComponent = () => { - const isBrowser = useIsBrowser(); - const url = isBrowser ? new URL(window.location.href) : undefined; - const someQueryParam = url?.searchParams.get('someParam'); - const [someParam, setSomeParam] = useState(someQueryParam || 'fallbackValue'); - - return {someParam}; -}; - -// highlight-start -const MyComponentParent = () => { - const isBrowser = useIsBrowser(); - - if (!isBrowser) { - return null; - } - - return ; -}; -// highlight-end - -export default MyComponentParent; -``` - -There are a couple more alternative solutions to this problem. However all of them require adding checks in **the parent component**: - -1. You can wrap `` with [`BrowserOnly`](./docusaurus-core.mdx#browseronly) -2. You can use `canUseDOM` from [`ExecutionEnvironment`](./docusaurus-core.mdx#executionenvironment) and `return null` when `canUseDOM` is `false` - ### `useBaseUrl` {#useBaseUrl} React hook to prepend your site `baseUrl` to a string.