Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React: Add support for react18's new root API #17215

Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ae474c2
Add possibility of using new React Root Api
Jan 12, 2022
fee8459
Reverse logic of callback div in CallbackWrapper
Jan 13, 2022
fc5128f
Use FRAMEWORK_OPTIONS to use new React Root API conditionally
Jan 13, 2022
983534d
docs: Add documentation to faq page
valentinpalkovic Jan 26, 2022
0479139
Update docs/faq.md
valentinpalkovic Feb 10, 2022
d1d8cff
Update docs/faq.md
valentinpalkovic Feb 10, 2022
b50177d
Rename newREactRootApi to newRootApi
Feb 10, 2022
afed20a
Prevent memory leaks and possible race conditions
Feb 10, 2022
505f99b
Transform class-based CallbackWrapper to function-based component
Feb 10, 2022
ca4bced
Simplify structure of getReactRoot
Feb 10, 2022
4899f37
Use setTimeout instead of CallbackWrapper
Feb 10, 2022
5e3f711
Rename types field from newReactRootApi to newRootApi
Feb 10, 2022
55ec6d4
Merge branch 'next' into render-react-components-with-new-root-api
yannbf Mar 25, 2022
b908cf3
add react 18 config to e2e scripts
yannbf Mar 25, 2022
544dfd5
Adjust loading of new root api to newest React 18.0.0 release
Apr 1, 2022
d1e4a71
Merge branch 'next' into pr/17215
shilman Apr 3, 2022
cd73c30
Merge branch 'next' into render-react-components-with-new-root-api
valentinpalkovic Apr 4, 2022
aedb6f6
Replace opt-in ReactRoot Api flag by opt-out flag
Apr 5, 2022
c0b53a5
Fix dynamic import resolution
Apr 5, 2022
677c189
wip
ndelangen Apr 5, 2022
eb73376
I think this makes michael happy
ndelangen Apr 5, 2022
262409b
Fix lint
shilman Apr 6, 2022
699cd79
cleanup
ndelangen Apr 6, 2022
465dc92
cleanup
ndelangen Apr 6, 2022
4ab777e
Merge branch 'next' into ndelangen/temp-alternative-solution-react-dom
ndelangen Apr 6, 2022
3f2d50a
cleanup
ndelangen Apr 6, 2022
9a8ebd5
cleanup
ndelangen Apr 6, 2022
6bc6913
Merge branch 'next' into ndelangen/temp-alternative-solution-react-dom
ndelangen Apr 6, 2022
b907b9e
Fix corrupted webpack configuration
Apr 6, 2022
0b36354
Merge branch 'next' into pr/17215
shilman Apr 6, 2022
b7422ff
Update snapshots
shilman Apr 6, 2022
0f118a3
Support react experimental versions
Apr 6, 2022
bcb93ec
React: Add react18 new root API migration instructions
shilman Apr 7, 2022
40747e0
Merge branch 'render-react-components-with-new-root-api' of https://g…
shilman Apr 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@
},
"peerDependencies": {
"@babel/core": "^7.11.5",
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0"
"react": "^16.8.0 || >=17.0.0",
"react-dom": "^16.8.0 || >=17.0.0"
},
"peerDependenciesMeta": {
"@babel/core": {
Expand Down
54 changes: 52 additions & 2 deletions app/react/src/client/preview/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ import { ReactFramework } from './types-6-0';

const { FRAMEWORK_OPTIONS } = global;

// TODO: Remove IRoot declaration as soon as @types/react v17.x is used
interface IRoot {
render(children: React.ReactChild | Iterable<React.ReactNode>): void;
unmount(): void;
}

// A map of all rendered React 18 nodes
const nodes = new Map<Element, IRoot>();

export const render: ArgsStoryFn<ReactFramework> = (args, context) => {
const { id, component: Component } = context;
if (!Component) {
Expand All @@ -28,9 +37,50 @@ export const render: ArgsStoryFn<ReactFramework> = (args, context) => {

const renderElement = async (node: ReactElement, el: Element) =>
new Promise((resolve) => {
ReactDOM.render(node, el, () => resolve(null));
// Create Root Element conditionally for new React 18 Root Api
const root = getReactRoot(el);

if (root) {
root.render(node);
setTimeout(() => {
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
resolve(null);
}, 0);
} else {
ReactDOM.render(node, el, () => resolve(null));
}
});

const unmountElement = (el: Element) => {
const root = nodes.get(el);
if (root && FRAMEWORK_OPTIONS?.newRootApi) {
root.unmount();
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
nodes.delete(el);
} else {
ReactDOM.unmountComponentAtNode(el);
}
};

const getReactRoot = (el: Element): IRoot | null => {
if (!FRAMEWORK_OPTIONS?.newRootApi) {
return null;
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
}

if (!(ReactDOM as any).createRoot) {
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(
"Your React version doesn't support the new React Root Api. Please use react and react-dom in version 18.x or set the storybook feature 'newRootApi' to false"
);
}

let root = nodes.get(el);

if (!root) {
root = (ReactDOM as any).createRoot(el) as IRoot;
nodes.set(el, root);
}

return root;
};

class ErrorBoundary extends ReactComponent<{
showException: (err: Error) => void;
showMain: () => void;
Expand Down Expand Up @@ -92,7 +142,7 @@ export async function renderToDOM(
// https://github.com/storybookjs/react-storybook/issues/81
// (This is not the case when we change args or globals to the story however)
if (forceRemount) {
ReactDOM.unmountComponentAtNode(domElement);
unmountElement(domElement);
}

await renderElement(element, domElement);
Expand Down
5 changes: 5 additions & 0 deletions app/react/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ export interface StorybookConfig extends BaseConfig {
reactOptions?: {
fastRefresh?: boolean;
strictMode?: boolean;
/**
* Uses React 18's new root API (ReactDOM.createRoot)
* The new root API happens to be the gateway for accessing new features of React 18 and adds out-of-the-box improvements.
*/
newRootApi?: boolean;
valentinpalkovic marked this conversation as resolved.
Show resolved Hide resolved
};
}
22 changes: 21 additions & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ module.exports = {
💡 Fast Refresh only works in development mode with React 16.10 or higher.
</div>

### How do I setup the new React Context Root API with Storybook?

The new [React Root API](https://reactjs.org/docs/concurrent-mode-reference.html) which was introduced in React 18 is an opt-in feature that can be used in Storybook React.

You can set the following properties in your `.storybook/main.js` files:

```js
module.exports = {
reactOptions: {
newRootApi: true,
},
};
```

After enabling it, it is possible to use React's newest [concurrent features](https://reactjs.org/docs/concurrent-mode-intro.html).

<div class="aside">
💡 The new React Root API (React.createRoot) only works with React 18 and above.
</div>

### Why is there no addons channel?

A common error is that an addon tries to access the "channel", but the channel is not set. It can happen in a few different cases:
Expand Down Expand Up @@ -377,4 +397,4 @@ const StoryMeta: ComponentMeta<typeof Button> = {
export default meta;
```

Although valid, it introduces additional boilerplate code to the story definition. Instead, we're working towards implementing a safer mechanism based on what's currently being discussed in the following [issue](https://github.com/microsoft/TypeScript/issues/7481). Once the feature is released, we'll migrate our existing examples and documentation accordingly.
Although valid, it introduces additional boilerplate code to the story definition. Instead, we're working towards implementing a safer mechanism based on what's currently being discussed in the following [issue](https://github.com/microsoft/TypeScript/issues/7481). Once the feature is released, we'll migrate our existing examples and documentation accordingly.
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8233,8 +8233,8 @@ __metadata:
webpack: 4
peerDependencies:
"@babel/core": ^7.11.5
react: ^16.8.0 || ^17.0.0
react-dom: ^16.8.0 || ^17.0.0
react: ^16.8.0 || >=17.0.0
react-dom: ^16.8.0 || >=17.0.0
peerDependenciesMeta:
"@babel/core":
optional: true
Expand Down