Skip to content

feat(v2): add a way to exclude components from build-time prerendering - #2323

Merged
yangshun merged 11 commits into
masterfrom
feat-exclude-ssr
Mar 29, 2020
Merged

yangshun merged 11 commits into
masterfrom
feat-exclude-ssr

Conversation

@lex111

@lex111 lex111 commented Feb 26, 2020

Copy link
Copy Markdown
Contributor

Motivation

Resolve #2273.

Have you read the Contributing Guidelines on pull requests?

Yes

Test Plan

  1. Inside ClientOnly put component that used window object, eg.

Related PRs

(If this PR adds or changes functionality, please take some time to update the docs at https://github.com/facebook/docusaurus, and link to your PR here.)

@lex111 lex111 added the pr: new feature This PR adds a new API or behavior. label Feb 26, 2020
@lex111
lex111 requested a review from yangshun February 26, 2020 00:40
@lex111
lex111 requested a review from wgao19 as a code owner February 26, 2020 00:40
@facebook-github-bot facebook-github-bot added the CLA Signed Signed Facebook CLA label Feb 26, 2020
@docusaurus-bot

docusaurus-bot commented Feb 26, 2020

Copy link
Copy Markdown
Contributor

Deploy preview for docusaurus-2 ready!

Built with commit 63ee326

https://deploy-preview-2323--docusaurus-2.netlify.com

@yangshun yangshun changed the title feat(v2): add a way to exclude components from SSR within JSX feat(v2): add a way to exclude components from server prerendering within JSX Feb 26, 2020
import React, {useEffect, useState} from 'react';

const ClientOnly = ({children}) => {
const [isClient, setIsClient] = useState(false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think isComponentMounted will be a better name.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

import React, {useEffect, useState} from 'react';

const BrowserOnly = ({children}) => {
const [isClient, setIsClient] = useState(false);

@yangshun yangshun Feb 26, 2020

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw I'm wondering if we can use the ExecutionEnvironment.canUseDOM instead, then there's no need for a state right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so? This state is needed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the diff between doing your way and this:

function BrowserOnly({children}) => {
  return ExecutionEnvironment.canUseDOM && children;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seemed to me that all the work of this component is to use useEffect, isn't it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and I think we don't even need useEffect here. Can you try what I've proposed and see whether there's any difference? :/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that none of the options actually works, it really puzzled me and in complete dismay 🤒

P.S. For the test, I used the @u-wave/react-vimeo component in the MDX file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So even your current implementation doesn't work? Luckily we test out first lol. Maybe the component is running some side effects that relies on browser environments being present during import. In that case there's no way around it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly, rather why something does not work, this component on some dependencies.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some investigation I found out that build fails if a component using a window object is imported, how is this possible? Therefore the BrowserOnly component is useless here 🤷‍♂️

@yangshun yangshun changed the title feat(v2): add a way to exclude components from server prerendering within JSX feat(v2): add a way to exclude components from build-time prerendering within JSX Feb 26, 2020
@yangshun yangshun changed the title feat(v2): add a way to exclude components from build-time prerendering within JSX feat(v2): add a way to exclude components from build-time prerendering Feb 26, 2020

@yangshun yangshun left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went to test this component out and it doesn't seem to work. I'm not sure why but I think we can't let the children be JSX, we have to let it be a render function.

To reproduce the bug, add

<BrowserOnly>{window.location.pathname}</BrowserOnly>

to any page and try to build it. It will fail.

But if you do this:

<BrowserOnly>{() => window.location.pathname}</BrowserOnly>

along with the suggested change of doing children() within BrowserOnly.js, it will be fine.

Will need to change the docs also and say the contents have to be a render function. Will also be good to have tests.


import React, {useEffect, useState} from 'react';

const BrowserOnly = ({children}) => {

@yangshun yangshun Feb 29, 2020

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also works and is shorter IMO.

function BrowserOnly({children}) {
  return ExecutionEnvironment.canUseDOM && children != null && <>{children()}</>;
};

Let's use function declaration for functional components.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, although this does not solve the issue of importing components using window.

For example, if I just import this component in the MDX file, then I will have an error during the building that the window is not defined.

import {Replay} from 'vimond-replay';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, users will have to lazy load the component after the component has mounted. There's no way we can handle that with the suggested component here.

@yangshun

Copy link
Copy Markdown
Contributor

@lex111 shall we try to get this into the next release?

@lex111

lex111 commented Mar 28, 2020

Copy link
Copy Markdown
Contributor Author

@yangshun done ✔️

@lex111 lex111 added this to the v2.0.0-alpha.49 milestone Mar 28, 2020

@yangshun yangshun left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will fix this if you don't get to it by tonight so that we can include this in the next release.

Comment thread website/docs/docusaurus-core.md Outdated
import BrowserOnly from '@docusaurus/BrowserOnly';

<BrowserOnly>
{/* Something that should be excluded during build process prerendering. */}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to give an example to show that children should be a render function and not normal children.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, done ✔️

Comment thread website/docs/docusaurus-core.md Outdated
@yangshun
yangshun merged commit 8143af6 into master Mar 29, 2020
@yangshun
yangshun deleted the feat-exclude-ssr branch March 29, 2020 15:44
mrizwanashiq pushed a commit to mrizwanashiq/docusaurus that referenced this pull request Jun 25, 2026
facebook#2323)

* feat(v2): add a way to exclude components from SSR within JSX

* Update docusaurus-core.md

* Update and rename ClientOnly.js to BrowserOnly.js

* improve docs

* Improve naming

* Simplify realization

* Fix code style

* Fix docs

* Update docusaurus-core.md

Co-authored-by: Yangshun Tay <tay.yang.shun@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Signed Facebook CLA pr: new feature This PR adds a new API or behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v2] add a way to exclude components from SSR within JSX

4 participants