Skip to content

Commit 363eb12

Browse files
committed
ld/hook-for-client-state
1 parent a78947f commit 363eb12

32 files changed

+102
-128
lines changed

docs/development.md

+9-12
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ Sometimes data is needed by the client to render a specific component, e.g. an e
239239
making it possible to access data further down a component tree without having to manually pass props down at each level.
240240
- Pass the same data as JSON on the document, and use this for react hydration on the browser. Hydration is executed from the static bundle's entrypoint.
241241

242-
It's then possible to access the state through the [`useContext`](https://reactjs.org/docs/hooks-reference.html#usecontext) hook in a descendent component.
242+
It's then possible to access the state through the [`useClientState`](../src/client/lib/hooks/useClientState.ts) hook in a descendent component.
243243

244244
Here's an example of adding some test data to the client state.
245245

@@ -293,16 +293,15 @@ const clientStateFromRequestStateLocals = ({
293293
};
294294
```
295295

296-
It's then possible to access it somewhere in the React app using the [`useContext`](https://reactjs.org/docs/hooks-reference.html#usecontext) hook:
296+
It's then possible to access it somewhere in the React app using the [`useClientState`](../src/client/lib/hooks/useClientState.ts) hook:
297297

298298
```tsx
299-
import React, { useContext } from 'react';
300-
import { ClientStateContext } from '@/client/components/ClientState';
299+
import useClientState from '@/client/lib/hooks/useClientState';
301300

302301
// export some react component
303302
export const TestComponent = () => {
304303
// get the client state from the context
305-
const clientState: ClientState = useContext(ClientStateContext);
304+
const clientState = useClientState();
306305
// extract the data we need from the state
307306
const { test } = clientState;
308307

@@ -312,19 +311,18 @@ export const TestComponent = () => {
312311
};
313312
```
314313

315-
In most cases you want to `useContext` outside a presentation component, and pass in the values you need as a prop to the component. This allows us to independently render this component in tests/storybook without relying on the rest of the app and state.
314+
In most cases you want to `useClientState` outside a presentation component, and pass in the values you need as a prop to the component. This allows us to independently render this component in tests/storybook without relying on the rest of the app and state.
316315

317316
Here's a contrived example:
318317

319318
```tsx
320319
// container component
321-
import React, { useContext } from 'react';
322-
import { ClientStateContext } from '@/client/components/ClientState';
320+
import useClientState from '@/client/lib/hooks/useClientState';
323321

324322
// export some react component
325323
export const TestContainer = () => {
326324
// get the client state from the context
327-
const clientState: ClientState = useContext(ClientStateContext);
325+
const clientState = useClientState();
328326
// extract the data we need from the state
329327
const { test } = clientState;
330328

@@ -417,14 +415,13 @@ You can also use `addReturnUrlToPath` if all you need to add is the `returnUrl`,
417415
To access the query parameters on the client, you can use the [`ClientState`](../src/shared/model/ClientState.ts) to do so, as a `queryParams` object is available as a property on the `ClientState`. Again you can use the `addQueryParamsToPath` to convert the query parameters to a string, which can be appended on the client to a link/form action. Some contrived examples below:
418416

419417
```tsx
420-
import React, { useContext } from 'react';
421-
import { ClientStateContext } from '@/client/components/ClientState';
418+
import useClientState from '@/client/lib/hooks/useClientState';
422419
import { addQueryParamsToPath } from '@/shared/lib/queryParams';
423420

424421
// export some react component
425422
export const TestContainer = () => {
426423
// get the client state from the context
427-
const clientState: ClientState = useContext(ClientStateContext);
424+
const clientState = useClientState();
428425
// extract the queryParams we need from the state
429426
const { queryParams } = clientState;
430427
// extract the values we need from the queryParam

src/client/components/ABTestDemo.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import React, { useContext } from 'react';
1+
import React from 'react';
2+
23
import { useAB } from '@guardian/ab-react';
34
import { tests } from '@/shared/model/experiments/abTests';
45
import { exampleTest } from '@/shared/model/experiments/tests/example-test';
5-
import { ClientState } from '@/shared/model/ClientState';
6-
import { ClientStateContext } from './ClientState';
6+
import useClientState from '@/client/lib/hooks/useClientState';
77

88
export const ABTestDemo = () => {
99
// load the AB Hook
@@ -61,7 +61,7 @@ export const ABTestDemo = () => {
6161

6262
// Example:
6363
// Load the client state
64-
const clientState: ClientState = useContext(ClientStateContext);
64+
const clientState = useClientState();
6565
// Get the AB Tests from clientState, these are the AB Tests the user is in with
6666
// format is { [key: string]: { variant: string }}
6767
const { abTesting: { participations = {} } = {} } = clientState;

src/client/components/ClientState.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { createContext, FunctionComponent } from 'react';
2+
23
import { ClientState } from '@/shared/model/ClientState';
34

45
export const defaultClientState = {

src/client/components/CsrfFormField.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { css } from '@emotion/react';
54
import { error, space, textSans } from '@guardian/source-foundations';
65

@@ -12,7 +11,7 @@ const csrfErrorStyle = css`
1211
`;
1312

1413
export const CsrfFormField = () => {
15-
const clientState: ClientState = useContext(ClientStateContext);
14+
const clientState = useClientState();
1615

1716
const { pageData: { fieldErrors } = {} } = clientState;
1817

src/client/layouts/ConsentsLayout.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import React, { useContext, FunctionComponent } from 'react';
1+
import React, { FunctionComponent } from 'react';
22
import { Footer } from '@/client/components/Footer';
3-
import { ClientState } from '@/shared/model/ClientState';
4-
import { ClientStateContext } from '@/client/components/ClientState';
3+
import useClientState from '@/client/lib/hooks/useClientState';
54
import { css } from '@emotion/react';
65
import {
76
Button,
@@ -60,7 +59,7 @@ export const ConsentsLayout: FunctionComponent<ConsentsLayoutProps> = ({
6059
showContinueButton = true,
6160
}) => {
6261
const autoRow = getAutoRow(1, gridItemColumnConsents);
63-
const clientState: ClientState = useContext(ClientStateContext);
62+
const clientState = useClientState();
6463
const {
6564
pageData = {},
6665
globalMessage: { error, success } = {},

src/client/layouts/Main.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { PropsWithChildren, useContext } from 'react';
1+
import React, { PropsWithChildren } from 'react';
22
import { css } from '@emotion/react';
33
import {
44
from,
@@ -14,8 +14,7 @@ import {
1414
import { gridRow, gridItem, SpanDefinition } from '@/client/styles/Grid';
1515
import { Header } from '@/client/components/Header';
1616
import { Footer } from '@/client/components/Footer';
17-
import { ClientStateContext } from '@/client/components/ClientState';
18-
import { ClientState } from '@/shared/model/ClientState';
17+
import useClientState from '@/client/lib/hooks/useClientState';
1918

2019
interface MainLayoutProps {
2120
pageHeader?: string;
@@ -138,7 +137,7 @@ export const MainLayout = ({
138137
errorOverride,
139138
errorContext,
140139
}: PropsWithChildren<MainLayoutProps>) => {
141-
const clientState: ClientState = useContext(ClientStateContext);
140+
const clientState = useClientState();
142141
const {
143142
globalMessage: { error, success } = {},
144143
pageData: { geolocation } = {},

src/client/layouts/MainGrid.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import React, { useContext } from 'react';
1+
import React from 'react';
22
import { gridRow, gridItem, SpanDefinition } from '@/client/styles/Grid';
33
import { css } from '@emotion/react';
4-
import { ClientState } from '@/shared/model/ClientState';
5-
import { ClientStateContext } from '@/client/components/ClientState';
4+
import useClientState from '@/client/lib/hooks/useClientState';
65
import { SubHeader } from '@/client/components/SubHeader';
76
import { GlobalSuccess } from '@/client/components/GlobalSuccess';
87
import { ErrorSummary } from '@guardian/source-react-components-development-kitchen';
@@ -55,7 +54,7 @@ export const MainGrid = ({
5554
children,
5655
gridSpanDefinition,
5756
}: Props) => {
58-
const clientState: ClientState = useContext(ClientStateContext);
57+
const clientState = useClientState();
5958
const { globalMessage: { error, success } = {} } = clientState;
6059

6160
const successMessage = successOverride || success;
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { useContext } from 'react';
2+
import { ClientStateContext } from '@/client/components/ClientState';
3+
4+
export default () => useContext(ClientStateContext);

src/client/pages/ChangePasswordCompletePage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { ChangePasswordComplete } from '@/client/pages/ChangePasswordComplete';
54

65
export const ChangePasswordCompletePage = () => {
7-
const clientState: ClientState = useContext(ClientStateContext);
6+
const clientState = useClientState();
87
const { pageData = {} } = clientState;
98
const { returnUrl, email } = pageData;
109
return (

src/client/pages/ChangePasswordPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import React, { useContext, useEffect } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
1+
import React, { useEffect } from 'react';
32
import { useParams } from 'react-router-dom';
4-
import { ClientStateContext } from '@/client/components/ClientState';
3+
import useClientState from '@/client/lib/hooks/useClientState';
54
import { ChangePassword } from '@/client/pages/ChangePassword';
65
import { buildUrl, buildUrlWithQueryParams } from '@/shared/lib/routeUtils';
76
import { logger } from '@/client/lib/clientSideLogger';
87

98
export const ChangePasswordPage = () => {
10-
const clientState: ClientState = useContext(ClientStateContext);
9+
const clientState = useClientState();
1110
const {
1211
pageData: { email = '', fieldErrors = [], timeUntilTokenExpiry } = {},
1312
queryParams,

src/client/pages/ConsentsCommunicationPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { ConsentsCommunication } from '@/client/pages/ConsentsCommunication';
54

65
export const ConsentsCommunicationPage = () => {
7-
const clientState = useContext<ClientState>(ClientStateContext);
6+
const clientState = useClientState();
87

98
const { pageData = {} } = clientState;
109
const { consents = [] } = pageData;

src/client/pages/ConsentsConfirmationPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { ConsentsConfirmation } from '@/client/pages/ConsentsConfirmation';
54
import { Consents } from '@/shared/model/Consent';
65

76
export const ConsentsConfirmationPage = () => {
8-
const clientState: ClientState = useContext(ClientStateContext);
7+
const clientState = useClientState();
98
const { pageData = {}, globalMessage: { error, success } = {} } = clientState;
109

1110
const {

src/client/pages/ConsentsDataPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import React, { useContext } from 'react';
2-
import { ClientStateContext } from '@/client/components/ClientState';
3-
import { ClientState } from '@/shared/model/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { Consents } from '@/shared/model/Consent';
54
import { ConsentsData } from '@/client/pages/ConsentsData';
65

76
export const ConsentsDataPage = () => {
8-
const clientState = useContext<ClientState>(ClientStateContext);
7+
const clientState = useClientState();
98

109
const { pageData = {} } = clientState;
1110
const { consents = [] } = pageData;

src/client/pages/ConsentsNewslettersPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import React, { useContext } from 'react';
2-
import { ClientStateContext } from '@/client/components/ClientState';
3-
import { ClientState } from '@/shared/model/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { ConsentsNewsletters } from '@/client/pages/ConsentsNewsletters';
54

65
export const ConsentsNewslettersPage = () => {
7-
const clientState = useContext<ClientState>(ClientStateContext);
6+
const clientState = useClientState();
87
const newsletters = clientState?.pageData?.newsletters ?? [];
98

109
return <ConsentsNewsletters newsletters={newsletters} />;

src/client/pages/EmailSentPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { EmailSent } from '@/client/pages/EmailSent';
54
import { buildQueryParamsString } from '@/shared/lib/queryParams';
65

@@ -10,7 +9,7 @@ interface Props {
109
}
1110

1211
export const EmailSentPage = ({ noAccountInfo, formTrackingName }: Props) => {
13-
const clientState: ClientState = useContext(ClientStateContext);
12+
const clientState = useClientState();
1413
const {
1514
pageData = {},
1615
queryParams,

src/client/pages/RegistrationEmailSentPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { EmailSent } from '@/client/pages/EmailSent';
54
import { buildQueryParamsString } from '@/shared/lib/queryParams';
65

76
import { buildUrl, buildUrlWithQueryParams } from '@/shared/lib/routeUtils';
87

98
export const RegistrationEmailSentPage = () => {
10-
const clientState: ClientState = useContext(ClientStateContext);
9+
const clientState = useClientState();
1110
const {
1211
pageData = {},
1312
queryParams,

src/client/pages/RegistrationPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { Registration } from '@/client/pages/Registration';
54

65
export const RegistrationPage = () => {
7-
const clientState: ClientState = useContext(ClientStateContext);
6+
const clientState = useClientState();
87
const {
98
pageData = {},
109
recaptchaConfig,

src/client/pages/ResendEmailVerificationPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
import React, { useContext } from 'react';
1+
import React from 'react';
22
import { ResendEmailVerification } from '@/client/pages/ResendEmailVerification';
3-
import { ClientState } from '@/shared/model/ClientState';
4-
import { ClientStateContext } from '@/client/components/ClientState';
3+
import useClientState from '@/client/lib/hooks/useClientState';
54

65
export const ResendEmailVerificationPage = () => {
76
const {
87
globalMessage: { success } = {},
98
pageData: { email, signInPageUrl } = {},
109
recaptchaConfig,
11-
} = useContext<ClientState>(ClientStateContext);
10+
} = useClientState();
1211

1312
const { recaptchaSiteKey } = recaptchaConfig;
1413

src/client/pages/ResendPasswordPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import React, { useContext, useEffect } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React, { useEffect } from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { ResetPassword } from '@/client/pages/ResetPassword';
54
import { MainBodyText } from '@/client/components/MainBodyText';
65
import { logger } from '@/client/lib/clientSideLogger';
76

87
export const ResendPasswordPage = () => {
9-
const clientState: ClientState = useContext(ClientStateContext);
8+
const clientState = useClientState();
109
const {
1110
pageData: { email = '' } = {},
1211
queryParams,

src/client/pages/ResetPasswordPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import React, { useContext } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { ResetPassword } from '@/client/pages/ResetPassword';
54
import { MainBodyText } from '@/client/components/MainBodyText';
65

76
export const ResetPasswordPage = () => {
8-
const clientState: ClientState = useContext(ClientStateContext);
7+
const clientState = useClientState();
98
const {
109
pageData: { email = '' } = {},
1110
queryParams,

src/client/pages/ResetPasswordSessionExpiredPage.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import React, { useContext, useEffect } from 'react';
2-
import { ClientState } from '@/shared/model/ClientState';
3-
import { ClientStateContext } from '@/client/components/ClientState';
1+
import React, { useEffect } from 'react';
2+
import useClientState from '@/client/lib/hooks/useClientState';
43
import { ResetPassword } from '@/client/pages/ResetPassword';
54
import { MainBodyText } from '@/client/components/MainBodyText';
65
import { logger } from '@/client/lib/clientSideLogger';
76

87
export const ResetPasswordSessionExpiredPage = () => {
9-
const clientState: ClientState = useContext(ClientStateContext);
8+
const clientState = useClientState();
109
const {
1110
pageData: { email = '' } = {},
1211
queryParams,

0 commit comments

Comments
 (0)