From 1e2616c613367ecc96a365b74c7cd0698b996e97 Mon Sep 17 00:00:00 2001 From: Jonas Galvez Date: Thu, 30 Jun 2022 20:31:07 -0300 Subject: [PATCH 1/5] feat(react): replace PassThrough with minipass --- packages/fastify-dx-react/package.json | 5 +++-- packages/fastify-dx-react/server/stream.js | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/fastify-dx-react/package.json b/packages/fastify-dx-react/package.json index 9b5f427..ebb4a67 100644 --- a/packages/fastify-dx-react/package.json +++ b/packages/fastify-dx-react/package.json @@ -27,10 +27,11 @@ "./plugin": "./plugin.cjs" }, "dependencies": { + "devalue": "^2.0.1", + "minipass": "^3.3.4", "react": "^18.1.0", "react-dom": "^18.1.0", "react-router-dom": "^6.3.0", - "devalue": "^2.0.1", "unihead": "^0.0.6", "valtio": "^1.6.1" }, @@ -45,4 +46,4 @@ "eslint-plugin-promise": "^4.3.1", "eslint-plugin-react": "^7.29.4" } -} \ No newline at end of file +} diff --git a/packages/fastify-dx-react/server/stream.js b/packages/fastify-dx-react/server/stream.js index ea4f94e..482cde6 100644 --- a/packages/fastify-dx-react/server/stream.js +++ b/packages/fastify-dx-react/server/stream.js @@ -1,7 +1,7 @@ -// Helper from the Node.js stream library to -// make it easier to work with renderToPipeableStream() -import { PassThrough } from 'stream' +// Helper to make the stream returned renderToPipeableStream() +// behave like an event emitter and facilitate error handling in Fastify +import Minipass from 'minipass' // React 18's preferred server-side rendering function, // which enables the combination of React.lazy() and Suspense @@ -15,13 +15,13 @@ export async function * generateHtmlStream ({ head, body, footer }) { yield chunk } } - yield footer + yield footer() } // Helper function to get an AsyncIterable (via PassThrough) // from the renderToPipeableStream() onShellReady event export function onShellReady (app) { - const duplex = new PassThrough() + const duplex = new Minipass() return new Promise((resolve, reject) => { try { const pipeable = renderToPipeableStream(app, { @@ -35,10 +35,10 @@ export function onShellReady (app) { }) } -// Helper function to get an AsyncIterable (via PassThrough) +// Helper function to get an AsyncIterable (via Minipass) // from the renderToPipeableStream() onAllReady event export function onAllReady (app) { - const duplex = new PassThrough() + const duplex = new Minipass() return new Promise((resolve, reject) => { try { const pipeable = renderToPipeableStream(app, { From 95603f0830a945b99aa0bacea15e81563333d761 Mon Sep 17 00:00:00 2001 From: Jonas Galvez Date: Thu, 30 Jun 2022 20:33:45 -0300 Subject: [PATCH 2/5] feat(react): move hydration after SSR phase --- packages/fastify-dx-react/index.js | 24 ++++++++++++------------ starters/react/client/index.html | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/fastify-dx-react/index.js b/packages/fastify-dx-react/index.js index e6673cc..597997b 100644 --- a/packages/fastify-dx-react/index.js +++ b/packages/fastify-dx-react/index.js @@ -55,20 +55,9 @@ export function createHtmlFunction (source, scope, config) { const soFooterTemplate = createHtmlTemplateFunction(soFooterSource) // This function gets registered as reply.html() return function ({ routes, context, body }) { - // Initialize hydration, which can stay empty if context.serverOnly is true - let hydration = '' // Decide which templating functions to use, with and without hydration const headTemplate = context.serverOnly ? soHeadTemplate : unHeadTemplate const footerTemplate = context.serverOnly ? soFooterTemplate : unFooterTemplate - // Decide whether or not to include the hydration script - if (!context.serverOnly) { - hydration = ( - '' - ) - } // Render page-level elements const head = new Head(context.head).render() // Create readable stream with prepended and appended chunks @@ -79,7 +68,18 @@ export function createHtmlFunction (source, scope, config) { : onAllReady(body) ), head: headTemplate({ ...context, head, hydration }), - footer: footerTemplate(context), + footer: () => footerTemplate({ + ...context, + // Decide whether or not to include the hydration script + ...!context.serverOnly && { + hydration: ( + '' + ) + }, + }), })) // Send out header and readable stream with full response this.type('text/html') diff --git a/starters/react/client/index.html b/starters/react/client/index.html index 19bcdb9..87c8a69 100644 --- a/starters/react/client/index.html +++ b/starters/react/client/index.html @@ -4,10 +4,10 @@ -
+ From fdc6a10ff3b2c3a0e304b4a0c92d8dc082868b97 Mon Sep 17 00:00:00 2001 From: Jonas Galvez Date: Thu, 30 Jun 2022 20:39:39 -0300 Subject: [PATCH 3/5] feat(react): ensure state can be modified in SSR phase --- packages/fastify-dx-react/index.js | 2 +- packages/fastify-dx-react/virtual/core.jsx | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/fastify-dx-react/index.js b/packages/fastify-dx-react/index.js index 597997b..3be8f71 100644 --- a/packages/fastify-dx-react/index.js +++ b/packages/fastify-dx-react/index.js @@ -67,7 +67,7 @@ export function createHtmlFunction (source, scope, config) { ? onShellReady(body) : onAllReady(body) ), - head: headTemplate({ ...context, head, hydration }), + head: headTemplate({ ...context, head }), footer: () => footerTemplate({ ...context, // Decide whether or not to include the hydration script diff --git a/packages/fastify-dx-react/virtual/core.jsx b/packages/fastify-dx-react/virtual/core.jsx index c930a49..03afa7e 100644 --- a/packages/fastify-dx-react/virtual/core.jsx +++ b/packages/fastify-dx-react/virtual/core.jsx @@ -6,15 +6,16 @@ import { proxy, useSnapshot } from 'valtio' import { waitResource, waitFetch } from '/dx:resource.js' import layouts from '/dx:layouts.js' -const isServer = typeof process === 'object' - +export const isServer = import.meta.env.SSR export const Router = isServer ? StaticRouter : BrowserRouter export const RouteContext = createContext({}) export function useRouteContext () { const routeContext = useContext(RouteContext) if (routeContext.state) { - routeContext.snapshot = useSnapshot(routeContext.state) + routeContext.snapshot = isServer + ? routeContext.state + : useSnapshot(routeContext.state) } return routeContext } @@ -56,7 +57,9 @@ export function DXRoute ({ head, ctxHydration, ctx, children }) { {children} @@ -130,7 +133,9 @@ export function DXRoute ({ head, ctxHydration, ctx, children }) { {children} From a21dc2be53da5c8821152b09eb7437349ff977bc Mon Sep 17 00:00:00 2001 From: Jonas Galvez Date: Thu, 30 Jun 2022 20:40:04 -0300 Subject: [PATCH 4/5] examples(react): state serialization --- starters/react/client/pages/index.jsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/starters/react/client/pages/index.jsx b/starters/react/client/pages/index.jsx index 2b36de5..1d9e575 100644 --- a/starters/react/client/pages/index.jsx +++ b/starters/react/client/pages/index.jsx @@ -1,5 +1,6 @@ import logo from '/assets/logo.svg' import { Link } from 'react-router-dom' +import { isServer, useRouteContext } from '/dx:core.jsx' export function getMeta () { return { @@ -8,10 +9,15 @@ export function getMeta () { } export default function Index () { + const { snapshot, state } = useRouteContext() + if (isServer) { + // State is automatically hydrated on the client + state.message = 'Welcome to Fastify DX for React!' + } return ( <> -

Welcome to Fastify DX for React!

+

{snapshot.message}

  • /using-data demonstrates how to leverage the getData() function From 408ec75a932d0c4e9c13b5c4b14c632f6eb024e3 Mon Sep 17 00:00:00 2001 From: Jonas Galvez Date: Thu, 30 Jun 2022 21:00:20 -0300 Subject: [PATCH 5/5] bump --- packages/fastify-dx-react/package.json | 2 +- starters/react/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fastify-dx-react/package.json b/packages/fastify-dx-react/package.json index ebb4a67..c2cb7a1 100644 --- a/packages/fastify-dx-react/package.json +++ b/packages/fastify-dx-react/package.json @@ -5,7 +5,7 @@ "type": "module", "main": "index.js", "name": "fastify-dx-react", - "version": "0.0.2", + "version": "0.0.3", "files": [ "virtual/create.jsx", "virtual/root.jsx", diff --git a/starters/react/package.json b/starters/react/package.json index 7eeff14..786ac70 100644 --- a/starters/react/package.json +++ b/starters/react/package.json @@ -10,7 +10,7 @@ "lint": "eslint . --ext .js,.jsx --fix" }, "dependencies": { - "fastify-dx-react": "^0.0.2", + "fastify-dx-react": "^0.0.3", "fastify-vite": "^3.0.0-beta.23", "ky-universal": "^0.10.1" },