diff --git a/client/index.js b/client/index.js index 70409012..1c99354e 100644 --- a/client/index.js +++ b/client/index.js @@ -6,6 +6,7 @@ import BrowserRouter from 'react-router-dom/BrowserRouter'; import asyncBootstrapper from 'react-async-bootstrapper'; import { AppContainer as ReactHotLoader } from 'react-hot-loader'; import { AsyncComponentProvider } from 'react-async-component'; +import { HelmetProvider } from 'react-helmet-async'; import './polyfills'; @@ -33,7 +34,9 @@ function renderApp(TheApp) { - + + + diff --git a/package-lock.json b/package-lock.json index 9f25a257..7c4e58c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4253,11 +4253,6 @@ "strip-eof": "1.0.0" } }, - "exenv": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", - "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" - }, "exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -10949,15 +10944,15 @@ "integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==", "dev": true }, - "react-helmet": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-5.2.0.tgz", - "integrity": "sha1-qBgR3yExOm1VxfBYxK66XW89l6c=", + "react-helmet-async": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-0.0.5.tgz", + "integrity": "sha512-O6neLxFoxp1fOziF+HEfIqlDkEmC9IAPJRQMG25y5BqDCxY/FTa2n9jUmN1zBmlSjHvDbIUlc5SokjojIJ96wg==", "requires": { "deep-equal": "1.0.1", - "object-assign": "4.1.1", + "invariant": "2.2.3", "prop-types": "15.6.1", - "react-side-effect": "1.1.5" + "shallowequal": "1.0.2" } }, "react-hot-loader": { @@ -11026,15 +11021,6 @@ "warning": "3.0.0" } }, - "react-side-effect": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz", - "integrity": "sha512-Z2ZJE4p/jIfvUpiUMRydEVpQRf2f8GMHczT6qLcARmX7QRb28JDBTpnM2g/i5y/p7ZDEXYGHWg0RbhikE+hJRw==", - "requires": { - "exenv": "1.2.2", - "shallowequal": "1.0.2" - } - }, "react-test-renderer": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.2.0.tgz", diff --git a/package.json b/package.json index 2e1fd654..4d9abcf8 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "react-async-bootstrapper": "2.1.0", "react-async-component": "2.0.0", "react-dom": "16.2.0", - "react-helmet": "5.2.0", + "react-helmet-async": "0.0.5", "react-hot-loader": "4.0.0", "react-router-dom": "4.2.2", "react-tree-walker": "4.0.2", diff --git a/server/middleware/reactApplication/ServerHTML.js b/server/middleware/reactApplication/ServerHTML.js index 4069aa17..1b3c85fe 100644 --- a/server/middleware/reactApplication/ServerHTML.js +++ b/server/middleware/reactApplication/ServerHTML.js @@ -45,7 +45,7 @@ function scriptTag(jsFilePath) { // COMPONENT function ServerHTML(props) { - const { asyncComponentsState, helmet, nonce, reactAppString } = props; + const { asyncComponentsState, helmet, nonce, children } = props; // Creates an inline script definition that is protected by the nonce. const inlineScript = body => ( @@ -124,8 +124,9 @@ function ServerHTML(props) { bodyElements={bodyElements.map((x, idx) => ( {x} ))} - appBodyString={reactAppString} - /> + > + {children} + ); } @@ -137,12 +138,12 @@ ServerHTML.propTypes = { // eslint-disable-next-line react/forbid-prop-types helmet: PropTypes.object, nonce: PropTypes.string, - reactAppString: PropTypes.string, + children: PropTypes.node, }; ServerHTML.defaultProps = { asyncComponentsState: undefined, helmet: undefined, nonce: undefined, - reactAppString: undefined, + children: null, }; diff --git a/server/middleware/reactApplication/index.js b/server/middleware/reactApplication/index.js index 52903d93..2a835473 100644 --- a/server/middleware/reactApplication/index.js +++ b/server/middleware/reactApplication/index.js @@ -1,10 +1,6 @@ import React from 'react'; -import Helmet from 'react-helmet'; -import { - renderToString, - renderToStaticMarkup, - renderToNodeStream, -} from 'react-dom/server'; +import { HelmetProvider } from 'react-helmet-async'; +import { renderToStaticMarkup, renderToNodeStream } from 'react-dom/server'; import { StaticRouter } from 'react-router-dom'; import { AsyncComponentProvider, @@ -53,28 +49,32 @@ export default function reactApplicationMiddleware(request, response) { // query for the results of the render. const reactRouterContext = {}; + // Holds Helmet state specific to each request + const helmetContext = {}; + // Declare our React application. - const app = ( + const App = () => ( - + + + ); // Pass our app into the react-async-component helper so that any async // components are resolved for the render. - asyncBootstrapper(app).then(() => { - const appString = renderToString(app); - + asyncBootstrapper(App()).then(() => { // Generate the html response. const html = renderToNodeStream( , + > + + , ); switch (reactRouterContext.status) { diff --git a/shared/components/DemoApp/AsyncAboutRoute/AboutRoute.js b/shared/components/DemoApp/AsyncAboutRoute/AboutRoute.js index 40041063..cbf4c49f 100644 --- a/shared/components/DemoApp/AsyncAboutRoute/AboutRoute.js +++ b/shared/components/DemoApp/AsyncAboutRoute/AboutRoute.js @@ -1,5 +1,5 @@ import React from 'react'; -import Helmet from 'react-helmet'; +import Helmet from 'react-helmet-async'; function AboutRoute() { return ( diff --git a/shared/components/DemoApp/AsyncAboutRoute/__tests__/__snapshots__/AboutRoute.test.js.snap b/shared/components/DemoApp/AsyncAboutRoute/__tests__/__snapshots__/AboutRoute.test.js.snap index 99fde33a..aeca5659 100644 --- a/shared/components/DemoApp/AsyncAboutRoute/__tests__/__snapshots__/AboutRoute.test.js.snap +++ b/shared/components/DemoApp/AsyncAboutRoute/__tests__/__snapshots__/AboutRoute.test.js.snap @@ -8,14 +8,14 @@ exports[` renders 1`] = ` } } > - About - +

Produced with diff --git a/shared/components/DemoApp/AsyncCounterRoute/CounterRoute.js b/shared/components/DemoApp/AsyncCounterRoute/CounterRoute.js index 49bbd4fd..4df9f9b3 100644 --- a/shared/components/DemoApp/AsyncCounterRoute/CounterRoute.js +++ b/shared/components/DemoApp/AsyncCounterRoute/CounterRoute.js @@ -1,5 +1,6 @@ /* eslint-disable react/no-unescaped-entities */ import React, { Component } from 'react'; +import Helmet from 'react-helmet-async'; class CounterRoute extends Component { constructor(props) { @@ -15,6 +16,9 @@ class CounterRoute extends Component { render() { return (

+ + Counter +

Counter

diff --git a/shared/components/DemoApp/AsyncHomeRoute/HomeRoute.js b/shared/components/DemoApp/AsyncHomeRoute/HomeRoute.js index ffe8b979..a324edf1 100644 --- a/shared/components/DemoApp/AsyncHomeRoute/HomeRoute.js +++ b/shared/components/DemoApp/AsyncHomeRoute/HomeRoute.js @@ -1,5 +1,5 @@ import React from 'react'; -import Helmet from 'react-helmet'; +import Helmet from 'react-helmet-async'; import config from '../../../../config'; diff --git a/shared/components/DemoApp/AsyncHomeRoute/__tests__/__snapshots__/HomeRoute.test.js.snap b/shared/components/DemoApp/AsyncHomeRoute/__tests__/__snapshots__/HomeRoute.test.js.snap index 8b557992..83586603 100644 --- a/shared/components/DemoApp/AsyncHomeRoute/__tests__/__snapshots__/HomeRoute.test.js.snap +++ b/shared/components/DemoApp/AsyncHomeRoute/__tests__/__snapshots__/HomeRoute.test.js.snap @@ -2,14 +2,14 @@ exports[` renders 1`] = `

- Home - +

Hello world!

diff --git a/shared/components/DemoApp/index.js b/shared/components/DemoApp/index.js index 5cf67ce8..f1a3fb5c 100644 --- a/shared/components/DemoApp/index.js +++ b/shared/components/DemoApp/index.js @@ -3,7 +3,7 @@ import 'normalize.css/normalize.css'; import React from 'react'; import Switch from 'react-router-dom/Switch'; import Route from 'react-router-dom/Route'; -import Helmet from 'react-helmet'; +import Helmet from 'react-helmet-async'; import config from '../../../config'; diff --git a/shared/components/HTML/index.js b/shared/components/HTML/index.js index 7e056071..f6e04feb 100644 --- a/shared/components/HTML/index.js +++ b/shared/components/HTML/index.js @@ -8,13 +8,13 @@ import PropTypes from 'prop-types'; * The is the HTML shell for our React Application. */ function HTML(props) { - const { htmlAttributes, headerElements, bodyElements, appBodyString } = props; + const { htmlAttributes, headerElements, bodyElements, children } = props; return ( {headerElements} -
+
{children}
{bodyElements} @@ -26,14 +26,14 @@ HTML.propTypes = { htmlAttributes: PropTypes.object, headerElements: PropTypes.node, bodyElements: PropTypes.node, - appBodyString: PropTypes.string, + children: PropTypes.node, }; HTML.defaultProps = { htmlAttributes: null, headerElements: null, bodyElements: null, - appBodyString: '', + children: null, }; // EXPORT