docusaurus icon indicating copy to clipboard operation
docusaurus copied to clipboard

Feature: Tabs dynamic defaultValue

Open slorber opened this issue 4 years ago • 0 comments

🚀 Feature

Docusaurus does not have a good API to support dynamic Tabs defaultValue, particularly where the value can only be computed on the browser.

The following code will lead to a SSR/client defaultValue mismatch for MacOs users:

const isMacOS =
  typeof window !== "undefined" && navigator.platform.startsWith("Mac");

<Tabs defaultValue={isMacOS ? "ios" : "android"}>
  <TabItem value="android" label="Android">
    Android content
  </TabItem>
  <TabItem value="ios" label="iOS">
    iOS content
  </TabItem>
</Tabs>

The result is that "android" will be used on the server/SSR, and React will try to hydrate with "ios".

On the client, React will have "ios" in comp state, but display "Android" in the DOM.

Even worst: we can't press the unselected "iOS" tab because setState("ios") is no-op for React when state already has this value: bug reported by @Simek 3 times on RN website: https://github.com/facebook/react-native-website/issues/2771

Problem visible here (requires macos + empty localstorage/incognito mode): https://deploy-preview-2804--react-native.netlify.app/docs/running-on-device


This issue only surfaced recently due to some recent React hydration optimization, by removing a duplicate React rendering that used to "fix" this issue.

image

I'm temporarily restoring the duplicate tabs rendering on hydration in https://github.com/facebook/docusaurus/pull/5652, so that it is fixed, but we need a proper design to solve this problem without any duplicate rendering (or at least avoid this duplicate rendering if the tab is the same on server/client)

Have you read the Contributing Guidelines on issues?

yes

Has this been requested on Canny?

no

Motivation

Officially support and document dynamic tabs defaultValue

API Design

To avoid any potential hydration nasty issues, we must ensure that server/client have the same value during SSR/hydration. The tab should only be updated after React has successfully hydrated.

I'm thinking of something like:

const isMacOS = () => navigator.platform.startsWith("Mac");

<Tabs defaultValue={(isBrowser) => isBrowser && isMacOS() ? "ios" : "android"}>
  <TabItem value="android" label="Android">
    Android content
  </TabItem>
  <TabItem value="ios" label="iOS">
    iOS content
  </TabItem>
</Tabs>

Also, seeing first android, and then having the UI magically switch to iOS after hydration is a bit weird for the user. As tabs are rendered eagerly by default, we should try to find a way to make the user see the ios tab even before React hydrates (this may not be simple 😅 ). Problem visible here:

  • https://deploy-preview-5652--docusaurus-2.netlify.app/tests/pages/tabs-tests
  • https://deploy-preview-2739--react-native.netlify.app/docs/running-on-device

Related links

  • 2024 conversion asking for a solution: https://github.com/facebook/docusaurus/discussions/10373

slorber avatar Oct 06 '21 17:10 slorber