Skip to content

Commit

Permalink
feat(react): upgrade to react-router v6
Browse files Browse the repository at this point in the history
BREAKING CHANGE: react-router API has changed (See migration guide: https://reactrouter.com/docs/en/v6/upgrading/v5)
  • Loading branch information
jaysoo authored and Jack Hsu committed Apr 14, 2022
1 parent e54eccb commit 1c64d19
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 35 deletions.
5 changes: 5 additions & 0 deletions docs/map.json
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,11 @@
"id": "using-tailwind-css-in-react",
"file": "shared/guides/using-tailwind-css-in-react"
},
{
"name": "React 18 Migration",
"id": "react-18",
"file": "shared/guides/react-18"
},
{
"name": "Using Tailwind CSS with Angular projects",
"id": "using-tailwind-css-with-angular-projects",
Expand Down
90 changes: 90 additions & 0 deletions docs/shared/guides/react-18.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# React 18 Migration

Workspaces that upgrade to Nx 14 will be automatically migrated to React 18. This migration will also include an upgrade to React Router v6, if it is used in the workspace.

Nx will automatically update your applications to use the new `react-dom-/client` API.

From this:

```typescript jsx
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom';
import App from './app/app';

ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
document.getElementById('root')
);
```

To this:

```typescript jsx
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './app/app';

const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<StrictMode>
<App />
</StrictMode>
);
```

There might be additional changes needed for your code to be fully compatible with React 18. If you use `React.FC` type (which Nx does not use), then you will need to
update your component props to include `children` explicitly.

Before:

```typescript jsx
interface MyButtonProps {
color: string;
}
```

After:

```typescript jsx
interface MyButtonProps {
color: string;
children?: React.ReactNode; // children is no longer implicitly provided by React.FC
}
```

For more information on React 18 migration, please see the [official guide](https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html).

## React Router v6

In addition to the React 18 migration, Nx will also update your workspace to React Router v6 -- assuming you use React Router v5 previously.
There are breaking changes in React Router v6. Please refer to the official [v5 to v6 guide](https://reactrouter.com/docs/en/v6/upgrading/v5) for details.

We highly recommend teams to upgrade their workspace to v6, but if you choose to opt out and continue to use v5, then you will need to disable React strict mode. Navigation is broken in strict mode for React Router v5 due to a transition issue.

To disable strict mode, open your `main.tsx` file and remove `<Strict>` in your render function.

Before:

```typescript jsx
root.render(
<Strict>
<BrowserRouter>
<App />
</BrowserRouter>
</Strict>
);
```

After (for React Router v5):

```typescript jsx
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
```
25 changes: 25 additions & 0 deletions packages/react/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,31 @@
"alwaysAddToPackageJson": false
}
}
},
"14.0.0": {
"version": "14.0.0-beta.1",
"packages": {
"@types/react": {
"version": "18.0.5",
"alwaysAddToPackageJson": false
},
"@emotion/babel-plugin": {
"version": "11.9.2",
"alwaysAddToPackageJson": false
},
"react-router-dom": {
"version": "6.3.0",
"alwaysAddToPackageJson": false
},
"@testing-library/react": {
"version": "13.0.1",
"alwaysAddToPackageJson": false
},
"@testing-library/react-hooks": {
"version": "8.0.0",
"alwaysAddToPackageJson": false
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<% if (strict) { %>import { StrictMode } from 'react';<% } %>
import * as ReactDOMClient from 'react-dom/client';
import * as ReactDOM from 'react-dom/client';
<% if (routing) { %>import { BrowserRouter } from 'react-router-dom';<% } %>

import App from './app/<%= fileName %>';

const root = ReactDOMClient.createRoot(document.getElementById('root') as HTMLElement);
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<% if (strict) { %><StrictMode><% } %><% if (routing) { %><BrowserRouter><% } %><App /><% if (routing) { %></BrowserRouter><% } %><% if (strict) { %></StrictMode><% } %>
);
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class <%= className %> extends Component<<%= className %>Props> {
<ul>
<li><Link to="/"><%= name %> root</Link></li>
</ul>
<Route path="/" render={() => <div>This is the <%= name %> root route.</div>} />
<Route path="/" element={<div>This is the <%= name %> root route.</div>} />
<% } %>
</<%= wrapper %>>
);
Expand All @@ -59,7 +59,7 @@ export function <%= className %>(props: <%= className %>Props) {
<ul>
<li><Link to="/"><%= name %> root</Link></li>
</ul>
<Route path="/" render={() => <div>This is the <%= name %> root route.</div>} />
<Route path="/" element={<div>This is the <%= name %> root route.</div>} />
<% } %>
</<%= wrapper %>>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import NxWelcome from "./nx-welcome";
<% if (remotes.length > 0) { %>
import { Link, Route, Switch } from 'react-router-dom';
import { Link, Route, Routes } from 'react-router-dom';

<% remotes.forEach(function(r) { %>
const <%= r.className %> = React.lazy(() => import('<%= r.fileName %>/Module'));
Expand All @@ -16,12 +16,12 @@ export function App() {
<li><Link to="/<%=r.fileName%>"><%=r.className%></Link></li>
<% }); %>
</ul>
<Switch>
<Route exact path="/" render={() => <NxWelcome title="<%= projectName %>"/>} />
<Routes>
<Route path="/" element={<NxWelcome title="<%= projectName %>"/>} />
<% remotes.forEach(function(r) { %>
<Route path="/<%=r.fileName%>" render={() => <<%= r.className %>/>} />
<Route path="/<%=r.fileName%>" element={<<%= r.className %>/>} />
<% }); %>
</Switch>
</Routes>
</React.Suspense>
);
}
Expand Down
18 changes: 9 additions & 9 deletions packages/react/src/mfe/mfe-ast-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ describe('addRemoteRoute', () => {
it('should add remote route to host app', async () => {
const sourceCode = stripIndents`
import * as React from 'react';
import { Link, Route, Switch } from 'react-router-dom';
import { Link, Route, Routes } from 'react-router-dom';
const App1 = React.lazy(() => import('app1/Module'));
Expand All @@ -127,9 +127,9 @@ describe('addRemoteRoute', () => {
<ul>
<li><Link to="/app1">App1</Link></li>
</ul>
<Switch>
<Route path="/app1" render={() => <App1 />} />
</Switch>
<Routes>
<Route path="/app1" element={<App1 />} />
</Routes>
</React.Suspense>
);
}
Expand All @@ -152,7 +152,7 @@ describe('addRemoteRoute', () => {
expect(result).toEqual(
stripIndents`
import * as React from 'react';
import { Link, Route, Switch } from 'react-router-dom';
import { Link, Route, Routes } from 'react-router-dom';
const App2 = React.lazy(() => import('app2/Module'));
Expand All @@ -165,10 +165,10 @@ describe('addRemoteRoute', () => {
<li><Link to="/app1">App1</Link></li>
<li><Link to="/app2">App2</Link></li>
</ul>
<Switch>
<Route path="/app1" render={() => <App1 />} />
<Route path="/app2" render={() => <App2 />} />
</Switch>
<Routes>
<Route path="/app1" element={<App1 />} />
<Route path="/app2" element={<App2 />} />
</Routes>
</React.Suspense>
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/mfe/mfe-ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function addRemoteRoute(
changes.push({
type: ChangeType.Insert,
index: firstRoute.end,
text: `\n<Route path="/${names.fileName}" render={() => <${names.className} />} />`,
text: `\n<Route path="/${names.fileName}" element={<${names.className} />} />`,
});

if (firstLink) {
Expand Down
14 changes: 14 additions & 0 deletions packages/react/src/migrations/update-14-0-0/update-14-0-0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {
addDependenciesToPackageJson,
logger,
readJson,
Tree,
} from '@nrwl/devkit';

export async function updateToReact18(host: Tree) {
logger.warn(
`Nx 14 comes with React Router v6, which breaks backward compatibility. For more information, refer to the guide here: https://nx.dev/guides/react-18#react-router-v6.`
);
}

export default updateToReact18;

0 comments on commit 1c64d19

Please sign in to comment.