Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexSergey committed Jan 13, 2021
1 parent a99b13c commit fd48dff
Showing 23 changed files with 70 additions and 64 deletions.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@ The easiest way to move your React application to Server Side Rendering.

## Getting Started
Modern JS applications are divided into 2 types:
- CSR - Client-Side rendering. The application will be displayed only after downloading and executing all the necessary JS code. Until then, the user will see a blank page. It degrades the UX and is bad for SEO.
- SSR - Server-Side rendering. The auxiliary server doesn't send a blank page, but a page with data. Thus, the user can immediately start working with the application, and SEO bots will index the page.
- CSR - *Client-Side rendering*. The application will be displayed only after downloading and executing all the necessary JS code. Until then, the user will see a blank page. It degrades the UX and is bad for SEO.
- SSR - *Server-Side rendering*. The auxiliary server doesn't send a blank page, but a page with data. Thus, the user can immediately start working with the application, and SEO bots will index the page.

## SSR

@@ -32,18 +32,18 @@ Schematically, the SSR application looks like this:

## Problems

One of the key problems with SSR applications is asynchronous operations. JS is an asynchronous language, all requests to the server, on which our application data depends, are asynchronous. They affect the state of the system - these are side effects. Since content availability is critical for search engines, we must handle this asynchronous behavior. The React Server Renderer is designed as a synchronous operation that steps through our React-DOM step by step and turns it into HTML.
One of the key problem with SSR applications is asynchronous operations. JS is an asynchronous language, all requests to the server, on which our application data depends, are asynchronous. They affect the state of the system - these are side effects. Since content availability is critical for search engines, we must handle this asynchronous behavior. The React Server Renderer is designed as a synchronous operation that steps through our React-DOM step by step and turns it into HTML.

The second problem is hydration. A process that allows us to associate the received HTML and the state of the application from the server with what will be built in the user's browser.

**iSSR** handles asynchronous operations and synchronizes state on the client.

## Motivation
React currently has many solutions for building SSR applications. The most popular solution is **Next.JS**. This is a great framework with many possibilities, iSSR cannot replace it. But, **Next.JS** is about completely rewriting your existing application. **Next.JS** is a framework, which means you have to use its approaches. **iSSR** is just a small library that handles side effects and synchronizes state.
- You can very quickly migrate your existing application to SSR using iSSR without major changes.
- You can very quickly migrate your existing application to SSR using **iSSR** without major changes.
- You can use any build system.
- You can use any state management solution like Redux, Apollo, Mobx or native setState.
- You can use any other SSR libraries (for example @loadable, react-helmet ...).
- You can use any other SSR libraries (for example @loadable, react-helmet, etc).

## Using

@@ -96,7 +96,13 @@ It's very simple, when we open the application it will load the todo list data f
```sh
# NPM
npm install @issr/core --save
npm install @babel/core @babel/preset-react @issr/babel-plugin babel-loader webpack webpack-cli nodemon-webpack-plugin --save-dev
npm install @issr/babel-plugin --save-dev
```

Webpack basic configuration for SSR:
```sh
# NPM
npm install @babel/core @babel/preset-react babel-loader webpack webpack-cli nodemon-webpack-plugin --save-dev
```

*For this example we should install node-fetch because native **fetch** is not supporting **node.js**. Also, for the server we will use express, but you can use any module*
@@ -216,7 +222,7 @@ import { hydrate } from 'react-dom';
import createSsr from '@issr/core';
import { App } from './App';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
@@ -228,7 +234,7 @@ hydrate(

The code:
```js
const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);
```
Associates the state executed on the server with the application on the client. For correct work *useSsrState* on the client

6 changes: 3 additions & 3 deletions examples/1-simple-without-backend/src/index.jsx
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ const App = ({ children }) => {
};

(async () => {
const [SSR, getState, effectCollection] = createSsr({}, { onlyClient: true });
const SSR = createSsr({}, { onlyClient: true });

render(
<SSR>
@@ -32,8 +32,8 @@ const App = ({ children }) => {
</SSR>, document.getElementById('root')
);

await effectCollection.runEffects();
const [SSR2] = createSsr(getState(), { onlyClient: true });
await SSR.effectCollection.runEffects();
const SSR2 = createSsr(SSR.getState(), { onlyClient: true });

render(
<SSR2>
2 changes: 1 addition & 1 deletion examples/10-redux-sagas/src/client.jsx
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import createSsr from '@issr/core';
import createStore from './store';
import rest from './utils/rest';

const [SSR] = createSsr();
const SSR = createSsr();

const { store } = createStore({
rest,
2 changes: 1 addition & 1 deletion examples/10.1-redux-thunk/src/client.jsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { App } from './App';
import createSsr from '@issr/core';
import createStore from './store';

const [SSR] = createSsr();
const SSR = createSsr();

const { store } = createStore({
initState: window.REDUX_DATA
2 changes: 1 addition & 1 deletion examples/11-apollo-graphql/src/client.jsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import { createHttpLink } from 'apollo-link-http';
import { App, resolvers, typeDefs } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr();
const SSR = createSsr();

const link = createHttpLink({
uri: 'http://localhost:3010'
2 changes: 1 addition & 1 deletion examples/12-dom-manipulations/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/13-nested-data-redux-graphql/src/client.jsx
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import createSsr from '@issr/core';
import createStore from './store';
import rest from './utils/rest';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

const link = createHttpLink({
uri: 'http://localhost:3010'
2 changes: 1 addition & 1 deletion examples/14-nested-async-effect/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/15-nested-async-effect-2/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/16-mobx/src/client.jsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import { CreateStoreProvider } from './Connect';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr();
const SSR = createSsr();

const { StoreProvider } = CreateStoreProvider(window.MOBX_DATA);

2 changes: 1 addition & 1 deletion examples/17-pure-webpack-5/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/2-simple-express/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/3-simple-koa/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/4-koa-react-router/src/client.jsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { createBrowserHistory } from 'history';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/5-meta-tags/src/client.jsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { createBrowserHistory } from 'history';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/5.1-meta-tags-helmet/src/client.jsx
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import { createBrowserHistory } from 'history';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/6-css-modules-other-styles/src/client.jsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { createBrowserHistory } from 'history';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/7-loadable-components/src/client.jsx
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import { createBrowserHistory } from 'history';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

loadableReady(() => (
hydrate(
2 changes: 1 addition & 1 deletion examples/8-error-handling/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
2 changes: 1 addition & 1 deletion examples/9-live-reload/src/client.jsx
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { hydrate } from 'react-dom';
import { App } from './App';
import createSsr from '@issr/core';

const [SSR] = createSsr(window.SSR_DATA);
const SSR = createSsr(window.SSR_DATA);

hydrate(
<SSR>
12 changes: 6 additions & 6 deletions packages/core/src/hooks.test.tsx
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import createSsr from './iSSR';

describe('hooks tests', () => {
test('useSsrEffect - Basic load on ready', async () => {
const [SSR, ,effectCollection] = createSsr();
const SSR = createSsr();
let called = false;

const App = (): JSX.Element => {
@@ -30,13 +30,13 @@ describe('hooks tests', () => {
</SSR>
).html();

await effectCollection.runEffects();
await SSR.effectCollection.runEffects();

expect(called).toBe(true);
});

test('useSsrState - Load state by source', async () => {
const [SSR] = createSsr({
const SSR = createSsr({
'custom-id': 'bar'
});

@@ -61,7 +61,7 @@ describe('hooks tests', () => {
});

test('useSsrState - use setState isomorphic', async () => {
const [SSR, getState, effectCollection] = createSsr();
const SSR = createSsr();

const App = (): JSX.Element => {
const [state, setState] = useSsrState('', 'state-0');
@@ -89,8 +89,8 @@ describe('hooks tests', () => {
)
.html();

await effectCollection.runEffects();
const state = getState();
await SSR.effectCollection.runEffects();
const state = SSR.getState();

const key = Object.keys(state)[0];

50 changes: 25 additions & 25 deletions packages/core/src/iSSR.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { createContext, useEffect, isValidElement } from 'react';
import React, { createContext, useEffect, isValidElement, FunctionComponent } from 'react';
import EffectCollection from './EffectCollection';
import { isBackend, clone } from './utils';

@@ -14,11 +14,10 @@ interface OptionsInterface {
onlyClient?: boolean;
}

type ReturnCreateISSR = [
({ children }: { children: JSX.Element }) => JSX.Element,
() => StateInterface,
EffectCollection
];
interface ReturnCreateISSR extends FunctionComponent {
getState: () => StateInterface;
effectCollection: EffectCollection;
}

interface IssrContextInterface {
isLoading: () => boolean;
@@ -69,25 +68,26 @@ const createSsr = (initState: InitStateInterface = {}, options: OptionsInterface

const getState = (): StateInterface => clone(app.state);

return [
({ children }): JSX.Element => (
<IssrContext.Provider value={{
isLoading,
initState,
effectCollection,
getState
}}
>
{children}
<OnComplete
loading={app.loading}
onLoad={onLoad}
/>
</IssrContext.Provider>
),
getState,
effectCollection
];
const iSSR = ({ children }): JSX.Element => (
<IssrContext.Provider value={{
isLoading,
initState,
effectCollection,
getState
}}
>
{children}
<OnComplete
loading={app.loading}
onLoad={onLoad}
/>
</IssrContext.Provider>
);

iSSR.getState = getState;
iSSR.effectCollection = effectCollection;

return iSSR;
};

export default createSsr;
8 changes: 4 additions & 4 deletions packages/core/src/server.tsx
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ export const serverRender = async (
iteration: (count?: number) => JSX.Element,
outsideEffects?: Function
): Promise<ServerRenderResult> => {
const [SSR, getState, effectCollection] = createSSR({ });
const SSR = createSSR({ });
const renderNested = async (): Promise<string> => {
const App = await iteration();

@@ -25,7 +25,7 @@ export const serverRender = async (
</SSR>
));

const waited = effectCollection.getWaited();
const waited = SSR.effectCollection.getWaited();

if (typeof outsideEffects === 'function') {
await outsideEffects();
@@ -36,7 +36,7 @@ export const serverRender = async (
}

if (waited.length > 0) {
await effectCollection.runEffects();
await SSR.effectCollection.runEffects();

return await renderNested();
}
@@ -48,6 +48,6 @@ export const serverRender = async (

return {
html,
state: getState()
state: SSR.getState()
};
};

0 comments on commit fd48dff

Please sign in to comment.