Skip to content

Commit 31ced14

Browse files
sserratablindaa121
andauthored
[Bug] Support SSR for ApiItem (#314)
* Add createStoreWithoutState * Refactor to support SSR and client-side hydration * Remove comment * Use isBrowser to conditionally render ApiDemoPanel * Experimenting with not persisting content-type and accept * Remove schema tab groupId and update TODO Co-authored-by: Bryan <[email protected]>
1 parent 74c5c52 commit 31ced14

File tree

4 files changed

+58
-44
lines changed

4 files changed

+58
-44
lines changed

packages/docusaurus-plugin-openapi-docs/src/markdown/createResponseSchema.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,8 @@ export function createResponseSchema({ title, body, ...rest }: Props) {
746746
value: `${mimeType}`,
747747
children: [
748748
create("SchemaTabs", {
749-
groupId: "schema-tabs",
749+
// TODO: determine if we should persist this
750+
// groupId: "schema-tabs",
750751
children: [
751752
firstBody &&
752753
create("TabItem", {

packages/docusaurus-theme-openapi-docs/src/theme/ApiDemoPanel/persistanceMiddleware.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,14 @@ export function createPersistanceMiddleware(options: ThemeConfig["api"]) {
4040
}
4141
}
4242

43-
if (action.type === "contentType/setContentType") {
44-
storage.setItem("contentType", action.payload);
45-
}
43+
// TODO: determine way to rehydrate without flashing
44+
// if (action.type === "contentType/setContentType") {
45+
// storage.setItem("contentType", action.payload);
46+
// }
4647

47-
if (action.type === "accept/setAccept") {
48-
storage.setItem("accept", action.payload);
49-
}
48+
// if (action.type === "accept/setAccept") {
49+
// storage.setItem("accept", action.payload);
50+
// }
5051

5152
if (action.type === "server/setServer") {
5253
storage.setItem("server", action.payload);

packages/docusaurus-theme-openapi-docs/src/theme/ApiItem/index.tsx

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import BrowserOnly from "@docusaurus/BrowserOnly";
1111
import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment";
1212
import { HtmlClassNameProvider } from "@docusaurus/theme-common";
1313
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
14+
import useIsBrowser from "@docusaurus/useIsBrowser";
1415
import type { Props } from "@theme/DocItem";
15-
import clsx from "clsx";
1616
import { ServerObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types";
1717
import type { ApiItem as ApiItemType } from "docusaurus-plugin-openapi-docs/lib/types";
1818
import { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
@@ -24,13 +24,11 @@ import { createAuth } from "../ApiDemoPanel/Authorization/slice";
2424
import { createPersistanceMiddleware } from "../ApiDemoPanel/persistanceMiddleware";
2525
import DocItemLayout from "./Layout";
2626
import DocItemMetadata from "./Metadata";
27-
import { createStoreWithState } from "./store";
27+
import { createStoreWithoutState, createStoreWithState } from "./store";
2828

2929
const { DocProvider } = require("@docusaurus/theme-common/internal");
3030

31-
let ApiDemoPanel = (_: { item: any; infoPath: any }) => (
32-
<div style={{ marginTop: "3.5em" }} />
33-
);
31+
let ApiDemoPanel = (_: { item: any; infoPath: any }) => <div />;
3432

3533
if (ExecutionEnvironment.canUseDOM) {
3634
ApiDemoPanel = require("@theme/ApiDemoPanel").default;
@@ -49,8 +47,19 @@ export default function ApiItem(props: Props): JSX.Element {
4947
const { siteConfig } = useDocusaurusContext();
5048
const themeConfig = siteConfig.themeConfig as ThemeConfig;
5149
const options = themeConfig.api;
50+
const isBrowser = useIsBrowser();
5251

53-
const ApiDocContent = () => {
52+
// Define store2
53+
let store2: any = {};
54+
const persistanceMiddleware = createPersistanceMiddleware(options);
55+
56+
// Init store for SSR
57+
if (!isBrowser) {
58+
store2 = createStoreWithoutState({}, [persistanceMiddleware]);
59+
}
60+
61+
// Init store for CSR to hydrate components
62+
if (isBrowser) {
5463
const acceptArray = Array.from(
5564
new Set(
5665
Object.values(api?.responses ?? {})
@@ -79,20 +88,20 @@ export default function ApiItem(props: Props): JSX.Element {
7988
securitySchemes: api?.securitySchemes,
8089
options,
8190
});
82-
const acceptValue = window?.sessionStorage.getItem("accept");
83-
const contentTypeValue = window?.sessionStorage.getItem("contentType");
91+
// TODO: determine way to rehydrate without flashing
92+
// const acceptValue = window?.sessionStorage.getItem("accept");
93+
// const contentTypeValue = window?.sessionStorage.getItem("contentType");
8494
const server = window?.sessionStorage.getItem("server");
8595
const serverObject = (JSON.parse(server!) as ServerObject) ?? {};
8696

87-
const persistanceMiddleware = createPersistanceMiddleware(options);
88-
const store2 = createStoreWithState(
97+
store2 = createStoreWithState(
8998
{
9099
accept: {
91-
value: acceptValue || acceptArray[0],
100+
value: acceptArray[0],
92101
options: acceptArray,
93102
},
94103
contentType: {
95-
value: contentTypeValue || contentTypeArray[0],
104+
value: contentTypeArray[0],
96105
options: contentTypeArray,
97106
},
98107
server: {
@@ -106,50 +115,42 @@ export default function ApiItem(props: Props): JSX.Element {
106115
},
107116
[persistanceMiddleware]
108117
);
109-
110-
return (
111-
<Provider store={store2}>
112-
<div className="row">
113-
<div className={clsx("col", api ? "col--7" : "col--12")}>
114-
<MDXComponent />
115-
</div>
116-
{api && (
117-
<div className="col col--5">
118-
<ApiDemoPanel item={api} infoPath={infoPath} />
119-
</div>
120-
)}
121-
</div>
122-
</Provider>
123-
);
124-
};
118+
}
125119

126120
if (api) {
127-
// TODO: determine if there's a way to SSR and hydrate ApiItem/ApiDemoPanel
128121
return (
129122
<DocProvider content={props.content}>
130123
<HtmlClassNameProvider className={docHtmlClassName}>
131124
<DocItemMetadata />
132125
<DocItemLayout>
133-
{
134-
<BrowserOnly fallback={<div />}>
135-
{() => {
136-
return <ApiDocContent />;
137-
}}
138-
</BrowserOnly>
139-
}
126+
<Provider store={store2}>
127+
<div className="row">
128+
<div className="col col--7">
129+
<MDXComponent />
130+
</div>
131+
<div className="col col--5">
132+
<BrowserOnly fallback={<div>Loading...</div>}>
133+
{() => {
134+
return <ApiDemoPanel item={api} infoPath={infoPath} />;
135+
}}
136+
</BrowserOnly>
137+
</div>
138+
</div>
139+
</Provider>
140140
</DocItemLayout>
141141
</HtmlClassNameProvider>
142142
</DocProvider>
143143
);
144144
}
145+
145146
// Non-API docs
146147
return (
147148
<DocProvider content={props.content}>
148149
<HtmlClassNameProvider className={docHtmlClassName}>
149150
<DocItemMetadata />
150151
<DocItemLayout>
151152
<div className="row">
152-
<div className={clsx("col col--12")}>
153+
<div className="col col--12">
153154
<MDXComponent />
154155
</div>
155156
</div>

packages/docusaurus-theme-openapi-docs/src/theme/ApiItem/store.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,15 @@ export const createStoreWithState = (
3838
getDefaultMiddleware().concat(...middlewares),
3939
});
4040

41+
export const createStoreWithoutState = (
42+
preloadedState: {},
43+
middlewares: any[]
44+
) =>
45+
configureStore({
46+
reducer: rootReducer,
47+
preloadedState,
48+
middleware: (getDefaultMiddleware) =>
49+
getDefaultMiddleware().concat(...middlewares),
50+
});
51+
4152
export type AppDispatch = ReturnType<typeof createStoreWithState>["dispatch"];

0 commit comments

Comments
 (0)