Skip to content

Commit 9da1b99

Browse files
committed
Add previewModeContext and create usePreviewMode hook
1 parent ef47083 commit 9da1b99

File tree

8 files changed

+106
-25
lines changed

8 files changed

+106
-25
lines changed

src/components/appBootstrap/MultiversalAppBootstrap.tsx

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import customerContext from '../../stores/customerContext';
1010
import i18nContext from '../../stores/i18nContext';
1111
import { Theme } from '../../types/data/Theme';
1212
import { MultiversalAppBootstrapProps } from '../../types/nextjs/MultiversalAppBootstrapProps';
13-
import { MultiversalPageProps } from '../../types/pageProps/MultiversalPageProps';
1413
import { SSGPageProps } from '../../types/pageProps/SSGPageProps';
1514
import { SSRPageProps } from '../../types/pageProps/SSRPageProps';
1615
import { initCustomerTheme } from '../../utils/data/theme';
@@ -20,6 +19,7 @@ import DefaultErrorLayout from '../errors/DefaultErrorLayout';
2019
import BrowserPageBootstrap, { BrowserPageBootstrapProps } from './BrowserPageBootstrap';
2120
import ServerPageBootstrap, { ServerPageBootstrapProps } from './ServerPageBootstrap';
2221
import UniversalGlobalStyles from './UniversalGlobalStyles';
22+
import previewModeContext from '../../stores/previewModeContext';
2323

2424
const fileLabel = 'components/appBootstrap/MultiversalAppBootstrap';
2525
const logger = createLogger({
@@ -68,7 +68,19 @@ const MultiversalAppBootstrap: React.FunctionComponent<Props> = (props): JSX.Ele
6868
i18nTranslations,
6969
lang,
7070
locale,
71-
}: MultiversalPageProps = pageProps;
71+
}: SSGPageProps | SSRPageProps = pageProps;
72+
let preview,
73+
previewData;
74+
75+
if ('preview' in pageProps) {
76+
// SSG
77+
preview = pageProps.preview;
78+
previewData = pageProps.previewData;
79+
} else {
80+
// SSR
81+
preview = false;
82+
previewData = null;
83+
}
7284

7385
if (!customer || !i18nTranslations || !lang || !locale) {
7486
// Unrecoverable error, we can't even display the layout because we don't have the minimal required information to properly do so
@@ -143,26 +155,28 @@ const MultiversalAppBootstrap: React.FunctionComponent<Props> = (props): JSX.Ele
143155
}
144156

145157
return (
146-
<i18nContext.Provider value={{ lang, locale }}>
147-
<customerContext.Provider value={customer}>
148-
{/* XXX Global styles that applies to all pages go there */}
149-
<UniversalGlobalStyles theme={theme} />
150-
151-
<ThemeProvider theme={theme}>
152-
{
153-
isBrowser() ? (
154-
<BrowserPageBootstrap
155-
{...browserPageBootstrapProps}
156-
/>
157-
) : (
158-
<ServerPageBootstrap
159-
{...serverPageBootstrapProps}
160-
/>
161-
)
162-
}
163-
</ThemeProvider>
164-
</customerContext.Provider>
165-
</i18nContext.Provider>
158+
<previewModeContext.Provider value={{ preview, previewData }}>
159+
<i18nContext.Provider value={{ lang, locale }}>
160+
<customerContext.Provider value={customer}>
161+
{/* XXX Global styles that applies to all pages go there */}
162+
<UniversalGlobalStyles theme={theme} />
163+
164+
<ThemeProvider theme={theme}>
165+
{
166+
isBrowser() ? (
167+
<BrowserPageBootstrap
168+
{...browserPageBootstrapProps}
169+
/>
170+
) : (
171+
<ServerPageBootstrap
172+
{...serverPageBootstrapProps}
173+
/>
174+
)
175+
}
176+
</ThemeProvider>
177+
</customerContext.Provider>
178+
</i18nContext.Provider>
179+
</previewModeContext.Provider>
166180
);
167181

168182
} else {

src/hooks/usePreviewMode.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import React from 'react';
2+
import previewModeContext, { PreviewModeContext } from '../stores/previewModeContext';
3+
4+
export type PreviewMode = PreviewModeContext
5+
6+
/**
7+
* Hook to access Next.js preview mode data
8+
*
9+
* Uses previewModeContext internally (provides an identical API)
10+
*
11+
* This hook should be used by components in favor of previewModeContext directly,
12+
* because it grants higher flexibility if you ever need to change the implementation (e.g: use something else than React.Context, like Redux/MobX)
13+
*
14+
* @see https://nextjs.org/docs/advanced-features/preview-mode
15+
*/
16+
const usePreviewMode = (): PreviewModeContext => {
17+
return React.useContext(previewModeContext);
18+
};
19+
20+
export default usePreviewMode;

src/stores/previewModeContext.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import { PreviewData } from '../types/nextjs/PreviewData';
3+
4+
export type PreviewModeContext = {
5+
preview: boolean;
6+
previewData: PreviewData;
7+
}
8+
9+
/**
10+
* Uses native React Context API
11+
*
12+
* @example Usage
13+
* import previewModeContext from './src/stores/previewModeContext';
14+
* const { preview, previewData }: PreviewModeContext = React.useContext(previewModeContext);
15+
*
16+
* @see https://reactjs.org/docs/context.html
17+
* @see https://medium.com/better-programming/react-hooks-usecontext-30eb560999f for useContext hook example (open in anonymous browser #paywall)
18+
*/
19+
export const previewModeContext = React.createContext<PreviewModeContext>(null);
20+
21+
export default previewModeContext;

src/types/nextjs/PreviewData.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Preview data type, set when preview mode is enabled
3+
* XXX Currently "unused", doesn't store any data
4+
*
5+
* Set to "undefined" when preview mode is disabled (by Next.js)
6+
* We override any falsy value to "null" to avoid a serialisation issue ("undefined" cannot be serialised)
7+
*
8+
* @see https://nextjs.org/docs/advanced-features/preview-mode#step-2-update-getstaticprops
9+
*/
10+
export type PreviewData = null | {};

src/types/nextjs/StaticPropsInput.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { StaticParams } from './StaticParams';
2+
import { PreviewData } from './PreviewData';
23

34
/**
45
* Static props given as inputs for getStaticProps
56
*/
67
export type StaticPropsInput = {
78
params?: StaticParams;
8-
preview?: boolean;
9-
previewData?: any; // eslint-disable-line @typescript-eslint/no-explicit-any
9+
preview: boolean;
10+
previewData: PreviewData;
1011
}

src/types/pageProps/SSGPageProps.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { MultiversalAppBootstrapPageProps } from '../nextjs/MultiversalAppBootstrapPageProps';
2+
import { PreviewData } from '../nextjs/PreviewData';
23
import { MultiversalPageProps } from './MultiversalPageProps';
34

45
/**
@@ -14,6 +15,8 @@ import { MultiversalPageProps } from './MultiversalPageProps';
1415
export type SSGPageProps<E extends {} = {}> = {
1516
// Props that are specific to SSG
1617
isStaticRendering: boolean;
18+
preview: boolean;
19+
previewData: PreviewData;
1720
} & MultiversalPageProps // Generic props that are provided immediately, no matter what
1821
& Partial<MultiversalAppBootstrapPageProps> // Pages served by SSG eventually benefit from props injected by the MultiversalAppBootstrap component
1922
& E;

src/utils/nextjs/SSG.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import { LAYOUT_QUERY } from '../../gql/common/layoutQuery';
66
import { supportedLocales } from '../../i18nConfig';
77
import { Customer } from '../../types/data/Customer';
88
import { I18nLocale } from '../../types/i18n/I18nLocale';
9+
import { PreviewData } from '../../types/nextjs/PreviewData';
910
import { StaticParams } from '../../types/nextjs/StaticParams';
1011
import { StaticPath } from '../../types/nextjs/StaticPath';
1112
import { StaticPathsOutput } from '../../types/nextjs/StaticPathsOutput';
12-
import { SSGPageProps } from '../../types/pageProps/SSGPageProps';
1313
import { StaticPropsInput } from '../../types/nextjs/StaticPropsInput';
1414
import { StaticPropsOutput } from '../../types/nextjs/StaticPropsOutput';
15+
import { SSGPageProps } from '../../types/pageProps/SSGPageProps';
1516
import { prepareGraphCMSLocaleHeader } from '../gql/graphcms';
1617
import { createApolloClient } from '../gql/graphql';
1718
import { DEFAULT_LOCALE, resolveFallbackLanguage } from '../i18n/i18n';
@@ -35,6 +36,8 @@ import { fetchTranslations, I18nextResources } from '../i18n/i18nextLocize';
3536
*/
3637
export const getCommonStaticProps: GetStaticProps<SSGPageProps, StaticParams> = async (props: StaticPropsInput): Promise<StaticPropsOutput> => {
3738
const customerRef: string = process.env.CUSTOMER_REF;
39+
const preview: boolean = props?.preview || false;
40+
const previewData: PreviewData = props?.previewData || null;
3841
const hasLocaleFromUrl = !!props?.params?.locale;
3942
const locale: string = hasLocaleFromUrl ? props?.params?.locale : DEFAULT_LOCALE; // If the locale isn't found (e.g: 404 page)
4043
const lang: string = locale.split('-')?.[0];
@@ -89,6 +92,8 @@ export const getCommonStaticProps: GetStaticProps<SSGPageProps, StaticParams> =
8992
isStaticRendering: true,
9093
lang,
9194
locale,
95+
preview,
96+
previewData,
9297
},
9398
// unstable_revalidate: false,
9499
};

src/utils/nextjs/previewMode.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { isBrowser } from '@unly/utils';
2+
3+
export const isPreviewModeEnabled = (): boolean => {
4+
if (isBrowser()) {
5+
return false;
6+
}
7+
};

0 commit comments

Comments
 (0)