serverside v1.5.9
serverside
Server-side rendering for apps based on inferno-scripts, react-scripts or react-scripts-ts without ejecting.
Usage
create-react-app my-app
cd my-app
yarn add serverside
Develop with yarn start
as usual. When you are done, run yarn serverside
to build and serve. Open http://localhost:3000 to view the resulting build in the browser.
Interesting usage
Typically features MobX and routing. Save a Servable.js
(or Servable.tsx
if using react-scripts-ts) near your index
:
import { autorun, isObservableProp } from "mobx";
// Inferno app? Just import its respective implementations.
import { Provider, useStaticRendering } from "mobx-react";
import { createElement } from "react";
import { renderToString } from "react-dom/server";
import { renderStatic } from "react-helmet";
import { matchPath, StaticRouter } from "react-router";
import { ClientServable } from "serverside";
import { App } from "./app/App";
import { Services } from "./app/Services";
export const Servable = new ClientServable({
// Your app component.
App,
Dom: {
createElement,
renderToString
},
// Optional.
Helmet: { renderStatic },
Mobx: {
Provider,
Services, // Your data and fetch logic, keep reading.
autorun,
isObservableProp,
useStaticRendering
},
Router: {
StaticRouter,
matchPath,
routes: App.routes // Your routes should receive services as props.
}
});
Load data with MobX
You need a Services class to store your state. Constructor must receive a Fetch API. The server passes an isomorphic-fetch, configured like a proxy, taking care of cookies etc.
import { decorate, observable } from "mobx";
import { ClientObservable } from "serverside";
export class Services {
constructor(fetch, initialState = {}) {
this.userService = new UserService(fetch);
new ClientObservable(this).copyFrom(initialState);
}
}
export class UserService {
constructor(fetch) {
this.fetch = fetch;
this.ip = "";
}
getIp = async () => {
const { origin } = await this.fetch("https://httpbin.org/ip");
this.ip = origin;
}
}
decorate(UserService, {
ip: observable
});
Put a Provider
in your index.js
:
const services = new Services(
fetch.bind(window),
window.__INITIAL_STATE__
);
ReactDOM.render(
<Provider {...services}>
<App />
</Provider>,
document.getElementById("root") // Make sure index.html has same id.
);
Create routes
Serverside supports react-helmet
and react-router
. Top-level route components can have loadData
.
// Item.js
import { inject, obsever } from "mobx-react";
import { ClientFetch } from "serverside";
class ItemView extends React.Component {
// Server
static async loadData({ match, itemService }) {
await itemService.getOne(match.params.id);
}
// Browser.
async componentDidMount() {
if (!this.props.itemService.items[this.props.match.params.id]) {
/**
* Same as ItemView.loadData(this.props), but all fetches happen on
* your server, so the client makes only one roundtrip.
*/
await new ClientFetch(this.props).askServer(Item);
}
}
render() {
const { items } = this.props.itemService;
const { params } = this.props.match;
const item = items[params.id] || { id: "Loading..." };
return <h1>{item.id}</h1>;
}
}
export const Item = inject("itemService")(observer(ItemView));
// App.js
import { Route, Switch } from "react-router";
export default class App extends React.Component {
render() {
return (
<Switch>
{App.routes.map((route, i) => <Route {...route} key={i} />)}
</Switch>
);
}
}
App.routes = [
{ path: "/items/:id", component: Item },
// ...
];
Further help
Also check out the Create React App README.
License
MIT
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago