Skip to content

Commit

Permalink
changes for v5 to work with React 18 and React Router's data routers
Browse files Browse the repository at this point in the history
  • Loading branch information
copleykj committed May 31, 2024
1 parent 9168e59 commit da5a0f9
Show file tree
Hide file tree
Showing 6 changed files with 3,485 additions and 2,083 deletions.
96 changes: 36 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@ Simple isomorphic React SSR for Meteor with subscribed data re-hydration

This project, like all of the projects maintained by the Meteor Community Packages org, takes time and hard work to keep updated. If you find this or any of our other packages useful, consider visiting the sponsor section of a repo and sending some love to the dedicated developers that keep your favorite packages up to date.

## Upgrades
### Upgrading from v3 to v4

### Upgrading from v2 to v3
Update to `react-router-dom` to v6

To better align with the default app that is created by the `meteor create` command. This package by default now renders into an element with an id of `react-target` where it used to render to and id of `react-app`, but is also now configurable. If your are upgrading from v2, you will need to either change the id in your html file, or use the `renderTarget` configuration option to set the renderTarget id to `react-app`.
### Upgrading from v4 to v5

```js
renderWithSSR(<App />, {
renderTarget: 'react-app',
});
```
This package now requires React 18 and uses Data Routers from React Router v5. Because of this there are several modifications that you will need to make to your app to upgrade. Also support for Redux and Styled Components has been removed from this package. You will need to find a new way to support them in your app.

### Upgrading from v3 to v4
The following steps are things you may need to do to upgrade your app from using v4 to v5 of this package:

Update to `react-router-dom` to v6
1. Update `react` to v18
2. Update `react-router-dom` to v6 if you haven't already
3. Install `abort-controller` with `npm install abort-controller`
4. Rewrite your root component to either be a JSX fragment containing `<Route>` components, or an array of Route object.
5. If you are using Redux or Styled Components, this package no longer has built in support for them. You will need to explore how to architect your app to support them.

## Install

1. First install NPM dependencies

```sh
npm install --save react react-dom react-router-dom react-helmet
npm install --save react@18 react-dom@18 react-router-dom@6 react-helmet@6 abort-controller@3
```

2. Install `communitypackages:react-router-ssr`
Expand All @@ -36,43 +36,26 @@ Update to `react-router-dom` to v6
meteor add communitypackages:react-router-ssr
```

> For `react-router-dom` v5 use v3 `communitypackages:react-router-ssr`.
>
> For `react-router-dom` v6 use v4 `communitypackages:react-router-ssr`.
## Package Exports 📦

**`renderWithSSR(rootComponent, [options])`** - Isomorphic app rendering.
**`renderWithSSR(routes, [options])`** - Isomorphic app rendering.

- `rootComponent` - The component that encompasses your application. Can be any React element. Routers and Switches are handled by the package so those aren't necessary inside your app.
- `routes` - A JSX element or array of JSX elements that represent the routes of your app.

- `options` - An object of rendering options. Currently there is only a single options, but there may be more options in the future.
- `options` - An object of rendering options.

- _`renderTarget`_ - A string specifying the `id` of the element to render your app into. Default is `react-target`

- _`storeOptions`_ - An object that contains the options for a redux store.

- `rootReducer` - Your apps root reducer.
- `initialState` - The initial state.
- `middlewares` - An array of middlewares to apply.

```js
import { renderWithSSR } from "meteor/communitypackages:react-router-ssr";

import thunk from "redux-thunk";
import { createLogger } from "redux-logger";

import rootReducer from "./reducers/root";
const AppRoutes = [
{ path: "/", element: <Home /> },
{ path: "/about", element: <About /> },
]

const logger = createLogger({ diff: true });

renderWithSSR(<App />, {
renderWithSSR(AppRoutes, {
renderTarget: 'react-app',
storeOptions: {
rootReducer,
initialState: { counter: 100 },
middlewares: [thunk, logger]
}
});
```

Expand All @@ -93,35 +76,28 @@ By default this package renders your app into an HTML element with an id of `rea

In shared code, such as in a `/both/main.jsx` file, or in a file that is imported into your `mainModule` for both the client and server..

```js
```jsx
import { renderWithSSR } from "meteor/communitypackages:react-router-ssr";
import { useTracker } from "meteor/react-meteor-data";

import React from "react";
import { Route, Routes } from "react-router-dom";
import DashboardPage from "./imports/ui/pages/dashbaord";
import ProfilePage from "./imports/ui/pages/profile";
import LoginPage from "./imports/ui/pages/login";

const App = () => {
const { user } = useTracker(() => ({
user: Meteor.user()
}));
if (user) {
return (
<Routes>
<Route exact path="/" element={DashboardPage} />
<Route path="/profile/:username" element={ProfilePage} />
</Routes>
);
}

return <LoginPage />;
};

renderWithSSR(<App />);
const AppRoutes = [
{ path: "/", element: <DashboardPage /> },
{ path: "/profile/:username", element: <ProfilePage /> },
{ path: "/login", element: <LoginPage /> },
];

// Alternatively you can use a JSX fragment
// const AppRoutes = (
// <>
// <Route path="/" element={<DashboardPage />} />
// <Route path="/profile/:username" element={<ProfilePage />} />
// <Route path="/login" element={<LoginPage />} />
// </>
// );

renderWithSSR(AppRoutes);
```

## Styled Components 💅

If the [styled-components](https://styled-components.com/) package is installed in your project, this package will detect it is present, create a new `ServerStyleSheet`, collect all styles, and use them to render your app.
50 changes: 12 additions & 38 deletions client.jsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,23 @@
import { FastRender } from 'meteor/communitypackages:fast-render';

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom/client';
import { RouterProvider, createRoutesFromElements, createBrowserRouter } from 'react-router-dom';

import './version-check';

let Provider;
let applyMiddleware;
let createStore;
const renderWithSSR = (routes, { renderTarget = 'react-target' } = {}) => {
if (!Array.isArray(routes)) {
routes = createRoutesFromElements(routes);
}

/* eslint-disable */
try {
({ Provider } = require('react-redux'));
({ createStore, applyMiddleware } = require('redux'));
} catch (e) {}
const router = createBrowserRouter(routes);

const renderWithSSR = (component, { renderTarget = 'react-target', storeOptions } = {}) => {
const AppJSX = <StrictMode><RouterProvider router={router} /></StrictMode>;

let ReactRouterSSR = () => (
<BrowserRouter>
{component}
</BrowserRouter>
);

if (storeOptions) {
const { rootReducer, middlewares } = storeOptions;
const appliedMiddlewares = middlewares ? applyMiddleware(...middlewares) : null;
const store = createStore(rootReducer, window.__PRELOADED_STATE__, appliedMiddlewares);

delete window.__PRELOADED_STATE__;

ReactRouterSSR = () => (
<Provider store={store}>
<BrowserRouter>
{component}
</BrowserRouter>
</Provider>
);
}

FastRender.onPageLoad(() => {
ReactDOM.hydrate(<ReactRouterSSR />, document.getElementById(renderTarget));
});
FastRender.onPageLoad(() => {
ReactDOM.hydrateRoot(document.getElementById(renderTarget), AppJSX);
});
};


export { renderWithSSR };
Loading

0 comments on commit da5a0f9

Please sign in to comment.