From 1cceeae068b55323cc0339163202f391ee7dcba5 Mon Sep 17 00:00:00 2001 From: POORYAJ Date: Mon, 29 Mar 2021 18:36:54 +0430 Subject: [PATCH 1/6] Fix 'getInitialPropsList' only accepting 'component' property --- src/server/handlers/ssr-handler.js | 7 +++++-- src/server/utils.js | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/server/handlers/ssr-handler.js b/src/server/handlers/ssr-handler.js index 2146343..5d0bfa0 100644 --- a/src/server/handlers/ssr-handler.js +++ b/src/server/handlers/ssr-handler.js @@ -13,7 +13,7 @@ import { initSSRContextValue } from 'src/services/initial-ssr-data/utils'; import { getEnv, isProd } from 'src/utils/env'; import AppRouter from 'src/AppRouter'; -import { findFinalComponent } from '../utils'; +import { findFinalComponent, getComponent } from '../utils'; import pageTemplate from '../templates/page-template.hbs'; // eslint-disable-next-line import/no-dynamic-require @@ -45,7 +45,10 @@ function renderPage({ appHtml, preloadedData, preloadedState }) { function getInitialPropsList({ renderBranch, store: { getState, dispatch }, req }) { return renderBranch.reduce( - (initialPropsList, { route: { component, path } }) => { + (initialPropsList, { route: { component: routeComponent, render: routeRenderer, path } }) => { + + const component = getComponent(routeComponent, routeRenderer); + // Due to usage of HOCs, we have to traverse the component tree // to find the final wrapped component which contains the static server-side methods const { diff --git a/src/server/utils.js b/src/server/utils.js index 672fc4e..6963c5e 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -20,3 +20,12 @@ export function findFinalComponent(component) { hasSSRData: isWrappedInWithSSRDataHOC, }; } + +export function getComponent(component, routeRenderer) { + // because route renderers are a wrapper around the actual component, + // they return the element created by react and not the actual component so we + // need to point to the actual component which is stored in the 'type' property + // in the returned react element + const reactElement = routeRenderer ? routeRenderer() : null; + return reactElement ? reactElement.type : component; +} From 0d6e620350ddf7862b1c7689f095199a4a7a2611 Mon Sep 17 00:00:00 2001 From: POORYAJ Date: Mon, 29 Mar 2021 19:24:53 +0430 Subject: [PATCH 2/6] Make a custom 'renderRoutes' --- src/AppRouter.jsx | 4 ++-- src/server/handlers/ssr-handler.js | 8 +++++-- src/server/utils.js | 36 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/AppRouter.jsx b/src/AppRouter.jsx index b1675fb..31c4b15 100644 --- a/src/AppRouter.jsx +++ b/src/AppRouter.jsx @@ -1,7 +1,7 @@ -import { renderRoutes } from 'react-router-config'; - import routes from 'src/configs/routes'; +import { renderRoutes } from './server/utils'; + function AppRouter() { return renderRoutes(routes); } diff --git a/src/server/handlers/ssr-handler.js b/src/server/handlers/ssr-handler.js index 5d0bfa0..5788bf1 100644 --- a/src/server/handlers/ssr-handler.js +++ b/src/server/handlers/ssr-handler.js @@ -53,17 +53,21 @@ function getInitialPropsList({ renderBranch, store: { getState, dispatch }, req // to find the final wrapped component which contains the static server-side methods const { component: { serverSideInitial }, - hasSSRData: hasPreloadedData, + hasSSRData: wrappedInWithSSRData, } = findFinalComponent(component); if (!serverSideInitial) { return initialPropsList; } + const dataPromise = serverSideInitial({ getState, dispatch, req }); + const serverSideInitialReturns = dataPromise !== undefined; + const hasPreloadedData = serverSideInitialReturns || wrappedInWithSSRData; + return initialPropsList.concat({ path, hasPreloadedData, - dataPromise: serverSideInitial({ getState, dispatch, req }), + dataPromise, }); }, [], diff --git a/src/server/utils.js b/src/server/utils.js index 6963c5e..01d6afc 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -1,5 +1,10 @@ +import React from 'react'; +import { Switch, Route } from 'react-router-dom'; + import { getEnv } from 'src/utils/env'; +import withSSRData from '../shared-components/withSSRData'; + // Due to usage of HOCs, we have to traverse the component tree to find the // final wrapped component which contains the static server-side methods // eslint-disable-next-line import/prefer-default-export @@ -29,3 +34,34 @@ export function getComponent(component, routeRenderer) { const reactElement = routeRenderer ? routeRenderer() : null; return reactElement ? reactElement.type : component; } + +export function renderRoutes(routes, extraProps = {}, switchProps = {}) { + if (!routes) return null; + + return ( + + {routes.map((route, index) => { + const { component: routeComponent, render: routeRenderer, ...rest } = route; + const component = getComponent(routeComponent, routeRenderer); + const { hasSSRData: wrappedInWithSSRData } = findFinalComponent(component); + const hasServerSideInitial = component.serverSideInitial; + const needsWithSSRData = hasServerSideInitial && !wrappedInWithSSRData; + + /* eslint react/no-array-index-key: 0 */ + return ( + { + const componentProps = { ...props, ...extraProps, route }; + + return (needsWithSSRData) + ? React.createElement(withSSRData(component), componentProps) + : React.createElement(component, componentProps) + }} + /> + ) + })} + + ); +} From 10dd7af997005c4cbc4209695b6b28d4f31d103e Mon Sep 17 00:00:00 2001 From: MohammadReza Iranmanesh Date: Sun, 6 Jun 2021 15:31:45 +0430 Subject: [PATCH 3/6] refactor: move withSSRData to services/initial-ssr-data --- src/server/utils.js | 10 +++++----- .../initial-ssr-data}/withSSRData.jsx | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) rename src/{shared-components => services/initial-ssr-data}/withSSRData.jsx (95%) diff --git a/src/server/utils.js b/src/server/utils.js index 01d6afc..6d1b35f 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -2,8 +2,7 @@ import React from 'react'; import { Switch, Route } from 'react-router-dom'; import { getEnv } from 'src/utils/env'; - -import withSSRData from '../shared-components/withSSRData'; +import withSSRData from 'src/services/initial-ssr-data/withSSRData'; // Due to usage of HOCs, we have to traverse the component tree to find the // final wrapped component which contains the static server-side methods @@ -28,10 +27,11 @@ export function findFinalComponent(component) { export function getComponent(component, routeRenderer) { // because route renderers are a wrapper around the actual component, - // they return the element created by react and not the actual component so we + // they return the element created by react and not the actual component so we // need to point to the actual component which is stored in the 'type' property // in the returned react element const reactElement = routeRenderer ? routeRenderer() : null; + return reactElement ? reactElement.type : component; } @@ -57,10 +57,10 @@ export function renderRoutes(routes, extraProps = {}, switchProps = {}) { return (needsWithSSRData) ? React.createElement(withSSRData(component), componentProps) - : React.createElement(component, componentProps) + : React.createElement(component, componentProps); }} /> - ) + ); })} ); diff --git a/src/shared-components/withSSRData.jsx b/src/services/initial-ssr-data/withSSRData.jsx similarity index 95% rename from src/shared-components/withSSRData.jsx rename to src/services/initial-ssr-data/withSSRData.jsx index fd27a24..452ca07 100644 --- a/src/shared-components/withSSRData.jsx +++ b/src/services/initial-ssr-data/withSSRData.jsx @@ -2,10 +2,11 @@ import React, { Component } from 'react'; import { withRouter } from 'react-router'; import PropTypes from 'prop-types'; -import { getContext } from 'src/services/initial-ssr-data'; import { getDisplayName } from 'src/utils/hoc'; import { getEnv } from 'src/utils/env'; +import { getContext } from './index'; + function withSSRData(WrappedComponent) { class WithSSRDataComponent extends Component { componentWillUnmount() { From 6ec296c9c91c03045cfebe44fa8d1aa15d7a764b Mon Sep 17 00:00:00 2001 From: MohammadReza Iranmanesh Date: Sun, 6 Jun 2021 15:31:58 +0430 Subject: [PATCH 4/6] style: lint --- src/server/handlers/ssr-handler.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/server/handlers/ssr-handler.js b/src/server/handlers/ssr-handler.js index 5788bf1..34a5498 100644 --- a/src/server/handlers/ssr-handler.js +++ b/src/server/handlers/ssr-handler.js @@ -45,8 +45,16 @@ function renderPage({ appHtml, preloadedData, preloadedState }) { function getInitialPropsList({ renderBranch, store: { getState, dispatch }, req }) { return renderBranch.reduce( - (initialPropsList, { route: { component: routeComponent, render: routeRenderer, path } }) => { - + ( + initialPropsList, + { + route: + { + component: routeComponent, + render: routeRenderer, path, + }, + }, + ) => { const component = getComponent(routeComponent, routeRenderer); // Due to usage of HOCs, we have to traverse the component tree From 67c30657679646d18bfa9b815db5831d11008608 Mon Sep 17 00:00:00 2001 From: MohammadReza Iranmanesh Date: Sun, 6 Jun 2021 15:32:12 +0430 Subject: [PATCH 5/6] fix: add path to routes --- src/configs/routes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/configs/routes.js b/src/configs/routes.js index cc02a98..f1fcb47 100644 --- a/src/configs/routes.js +++ b/src/configs/routes.js @@ -2,6 +2,7 @@ import Home from 'src/views/Home'; const routes = [ { + path: '/', component: Home, }, ]; From 9383daa5435465d70b9fa1bbb8ec8be5d4b7f2c4 Mon Sep 17 00:00:00 2001 From: MohammadReza Iranmanesh Date: Sun, 6 Jun 2021 15:32:25 +0430 Subject: [PATCH 6/6] doc: update README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fbfceb3..4fe9e2a 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Runs linter. Only views can be SSR. Views are components that directly connected to a URL. To do SSR you need just 2 simple steps: -* Add `serverSideInitial` static method to your view to do side-effects, dispatch actions to store or return values and promises. +* Just add `serverSideInitial` static method to your view to do side-effects, dispatch actions to store or return values and promises. ``` static serverSideInitial({ dispatch, req }) { @@ -85,7 +85,7 @@ Only views can be SSR. Views are components that directly connected to a URL. To ``` -* Wrap the view with `withSSRData` HOC to get value returned by `serverSideInitial` as `props.initialSSRData`. +* Now get value returned by `serverSideInitial` as `props.initialSSRData`. ``` Component.propTypes = { @@ -93,7 +93,7 @@ Component.propTypes = { posts: PropTypes.array(), }; -export default withSSRData(Component); +export default Component; ``` ## Directory Layout