feat: SPA redirect

This commit is contained in:
Fran Dios 2021-11-09 18:22:24 +09:00
parent 293a065ff7
commit 3fdcf6815b
3 changed files with 50 additions and 27 deletions

View file

@ -1,7 +1,7 @@
import React, {Suspense, useState} from 'react';
// @ts-ignore
import {createRoot} from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import {BrowserRouter, Redirect} from 'react-router-dom';
import type {ClientHandler} from './types';
import {ErrorBoundary} from 'react-error-boundary';
import {HelmetProvider} from 'react-helmet-async';
@ -35,7 +35,8 @@ function Content({clientWrapper: ClientWrapper}: {clientWrapper: any}) {
pathname: window.location.pathname,
search: window.location.search,
});
const response = useServerResponse(serverState);
const response = useServerResponse(serverState).read();
return (
<ServerStateProvider
@ -46,8 +47,12 @@ function Content({clientWrapper: ClientWrapper}: {clientWrapper: any}) {
<HelmetProvider>
<BrowserRouter>
<ServerStateRouter />
{/* @ts-ignore */}
<ClientWrapper>{response.read()}</ClientWrapper>
{response.redirect ? (
<Redirect to={response.redirect} />
) : (
/* @ts-ignore */
<ClientWrapper>{response}</ClientWrapper>
)}
</BrowserRouter>
</HelmetProvider>
</QueryProvider>

View file

@ -20,6 +20,7 @@ import {ServerComponentResponse} from './framework/Hydration/ServerComponentResp
import {ServerComponentRequest} from './framework/Hydration/ServerComponentRequest.server';
import {dehydrate} from 'react-query/hydration';
import {getCacheControlHeader} from './framework/cache';
import type {ServerResponse} from 'http';
/**
* react-dom/unstable-fizz provides different entrypoints based on runtime:
@ -118,25 +119,11 @@ const renderHydrogen: ServerHandler = (App, hook) => {
componentResponse.cacheControlHeader
);
const {customHead} = componentResponse;
if (customHead) {
if (customHead.headers) {
for (const [key, value] of Object.entries(customHead.headers)) {
response.setHeader(key, value);
}
}
if (customHead.statusText) {
response.statusMessage = customHead.statusText;
}
if (customHead.status) {
response.statusCode = customHead.status;
if (response.statusCode >= 300 && response.statusCode < 400) {
// Redirect
return response.end();
}
if (componentResponse.customHead) {
writeHeadToServerResponse(componentResponse, response);
if (response.statusCode >= 300 && response.statusCode < 400) {
// Redirect
return response.end();
}
}
@ -227,6 +214,15 @@ const renderHydrogen: ServerHandler = (App, hook) => {
* `template` and `script` tags inserted and rendered as part of the hydration response.
*/
onCompleteAll() {
if (componentResponse.customHead) {
writeHeadToServerResponse(componentResponse, response);
if (response.statusCode >= 300 && response.statusCode < 400) {
// Redirect: mock status to bypass fetch opaque responses
response.statusCode = 299;
return response.end();
}
}
// Tell React to start writing to the writer
startWriting();
@ -454,4 +450,23 @@ async function renderAppFromStringWithPrepass(
: body;
}
export function writeHeadToServerResponse(
{customHead = {}}: ServerComponentResponse,
serverResponse: ServerResponse
) {
if (customHead.headers) {
for (const [key, value] of Object.entries(customHead.headers)) {
serverResponse.setHeader(key, value);
}
}
if (customHead.statusText) {
serverResponse.statusMessage = customHead.statusText;
}
if (customHead.status) {
serverResponse.statusCode = customHead.status;
}
}
export default renderHydrogen;

View file

@ -35,10 +35,13 @@ function createFromFetch(fetchPromise: Promise<any>) {
if (!response.ok) {
throw new Error(`Hydration request failed: ${response.statusText}`);
}
return response.text();
})
.then((payload) => {
return convertHydrationResponseToReactComponents(payload);
// Mocked status to bypass fetch opaque responses
if (response.status === 299) {
return {redirect: response.headers.get('location')};
}
return response.text().then(convertHydrationResponseToReactComponents);
})
.catch((e) => {
console.error(e);