refactor(core): replace useDocusaurusContext().isClient by useIsBrowser() (#5349)

* extract separate useIsClient() hook

* for consistency, rename to `useIsBrowser`

* useless return

* improve doc for BrowserOnly

* update snapshot

* polish
This commit is contained in:
Sébastien Lorber 2021-08-12 19:02:29 +02:00 committed by GitHub
parent 69b11a8546
commit 295e77cc09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 213 additions and 90 deletions

View file

@ -109,19 +109,56 @@ const Home = () => {
### `<BrowserOnly/>` {#browseronly}
The `<BrowserOnly>` component accepts a `children` prop, a render function which will not be executed during the pre-rendering phase of the build process. This is useful for hiding code that is only meant to run in the browsers (e.g. where the `window`/`document` objects are being accessed). To improve SEO, you can also provide fallback content using the `fallback` prop, which will be prerendered until in the build process and replaced with the client-side only contents when viewed in the browser.
The `<BrowserOnly>` component permits to render React components only in the browser, after the React app has hydrated.
```jsx {1,5-10}
:::tip
Use it for integrating with code that can't run in Node.js, because `window` or `document` objects are being accessed.
:::
#### Props {#browseronly-props}
- `children`: render function prop returning browser-only JSX. Will not be executed in Node.js
- `fallback` (optional): JSX to render on the server (Node.js) and until React hydration completes.
#### Example with code {#browseronly-example-code}
```jsx
// highlight-start
import BrowserOnly from '@docusaurus/BrowserOnly';
// highlight-end
const MyComponent = () => {
return (
<BrowserOnly
fallback={<div>The fallback content to display on prerendering</div>}>
// highlight-start
<BrowserOnly>
{() => {
// Something that should be excluded during build process prerendering.
<span>page url = {window.location.href}</span>;
}}
</BrowserOnly>
// highlight-end
);
};
```
#### Example with a library {#browseronly-example-library}
```jsx
// highlight-start
import BrowserOnly from '@docusaurus/BrowserOnly';
// highlight-end
const MyComponent = (props) => {
return (
// highlight-start
<BrowserOnly fallback={<div>Loading...</div>}>
{() => {
const LibComponent = require('some-lib').LibComponent;
return <LibComponent {...props} />;
}}
</BrowserOnly>
// highlight-end
);
};
```
@ -132,7 +169,7 @@ A simple interpolation component for text containing dynamic placeholders.
The placeholders will be replaced with the provided dynamic values and JSX elements of your choice (strings, links, styled elements...).
#### Props {#props}
#### Props {#interpolate-props}
- `children`: text containing interpolation placeholders like `{placeholderName}`
- `values`: object containing interpolation placeholder values
@ -175,7 +212,7 @@ Apart the `values` prop used for interpolation, it is **not possible to use vari
:::
#### Props {#props-1}
#### Props {#translate-props}
- `children`: untranslated string in the default site locale (can contain [interpolation placeholders](#interpolate))
- `id`: optional value to use as key in JSON translation files
@ -253,7 +290,6 @@ interface DocusaurusContext {
globalData: Record<string, unknown>;
i18n: I18n;
codeTranslations: Record<string, string>;
isClient: boolean;
}
```
@ -275,6 +311,34 @@ const MyComponent = () => {
};
```
### `useIsBrowser` {#useIsBrowser}
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.
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/).
:::
Usage example:
```jsx
import React from 'react';
import useIsBrowser from '@docusaurus/useIsBrowser';
const MyComponent = () => {
// highlight-start
const isBrowser = useIsBrowser();
// highlight-end
return <div>{isBrowser ? 'Client' : 'Server'}</div>;
};
```
### `useBaseUrl` {#usebaseurl}
React hook to prepend your site `baseUrl` to a string.
@ -525,21 +589,27 @@ export default function Home() {
### `ExecutionEnvironment` {#executionenvironment}
A module which exposes a few boolean variables to check the current rendering environment. Useful if you want to only run certain code on client/server or need to write server-side rendering compatible code.
A module which exposes a few boolean variables to check the current rendering environment.
```jsx {2,5}
import React from 'react';
:::caution
For React rendering logic, use [`useIsBrowser()`](#useIsBrowser) or [`<BrowserOnly>`](#browseronly) instead.
:::
Example:
```jsx
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
const MyPage = () => {
const location = ExecutionEnvironment.canUseDOM ? window.location.href : null;
return <div>{location}</div>;
};
if (ExecutionEnvironment.canUseDOM) {
require('lib-that-only-works-client-side');
}
```
| Field | Description |
| --- | --- |
| `ExecutionEnvironment.canUseDOM` | `true` if on client, `false` if prerendering. |
| `ExecutionEnvironment.canUseDOM` | `true` if on client/browser, `false` on Node.js/prerendering. |
| `ExecutionEnvironment.canUseEventListeners` | `true` if on client and has `window.addEventListener`. |
| `ExecutionEnvironment.canUseIntersectionObserver` | `true` if on client and has `IntersectionObserver`. |
| `ExecutionEnvironment.canUseViewport` | `true` if on client and has `window.screen`. |