Skip to content

Commit 42d19b3

Browse files
authored
Merge pull request #11 from AlexStack/react19-nextjs15
React19 nextjs15
2 parents 6d0dd94 + ee760f7 commit 42d19b3

18 files changed

+666
-542
lines changed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ If you prefer Tailwind css, check this: [Tailwind-CSS-Version](https://github.co
2020

2121
## Clone this repository for React 19.x with NextJs 15.x or React 18.x with NextJs 14.x
2222

23-
- Clone React19-Next15-MUI6-TS-Starter:
24-
- `git clone -b react19-nextjs15 https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter.git react19-nextjs15-mui6-ts-starter`
25-
- Clone React18-Next14-MUI5-TS-Starter:
26-
- `git clone -b nextjs14 https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter.git react18-nextjs14-mui5-ts-starter`
23+
- Clone & install React19-Next15-MUI6-TS-Starter:
24+
- `git clone -b react19-nextjs15 https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter.git react19-nextjs15-mui6-ts-starter && cd react19-nextjs15-mui6-ts-starter && yarn install && yarn dev -p 3005`
25+
- Open <http://localhost:3005>
26+
- Note: React 19 is not released yet, there are some warnings in the console, please ignore them.
27+
- Clone & install React18-Next14-MUI5-TS-Starter:
28+
- `git clone -b nextjs14 https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter.git react18-nextjs14-mui5-ts-starter && cd react18-nextjs14-mui5-ts-starter && yarn install && yarn dev -p 3005`
29+
- Open <http://localhost:3005>
2730

2831
## Features
2932

package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@
2121
"@emotion/react": "^11.11.1",
2222
"@emotion/styled": "^11.11.0",
2323
"@hookform/resolvers": "^3.3.1",
24-
"@mui/icons-material": "^5.14.9",
25-
"@mui/material": "^5.14.10",
24+
"@mui/icons-material": "next",
25+
"@mui/material": "next",
2626
"dayjs": "^1.11.10",
27-
"next": "^15.0.0-rc.0",
28-
"react": "^19.0.0-rc-512b09b2-20240718",
29-
"react-dom": "^19.0.0-rc-512b09b2-20240718",
30-
"react-hook-form": "^7.46.2",
27+
"next": "rc",
28+
"react": "rc",
29+
"react-dom": "rc",
30+
"react-hook-form": "^7.52.2",
3131
"react-icons": "^4.10.1",
3232
"zod": "^3.22.4"
3333
},
@@ -38,11 +38,12 @@
3838
"@testing-library/jest-dom": "^5.16.5",
3939
"@testing-library/react": "^13.4.0",
4040
"@types/react": "^18.2.15",
41+
"@types/jest": "^29.5.12",
4142
"@typescript-eslint/eslint-plugin": "^5.62.0",
4243
"@typescript-eslint/parser": "^5.62.0",
4344
"autoprefixer": "^10.4.14",
4445
"eslint": "^8.45.0",
45-
"eslint-config-next": "^15.0.0-rc.0",
46+
"eslint-config-next": "rc",
4647
"eslint-config-prettier": "^8.8.0",
4748
"eslint-plugin-simple-import-sort": "^7.0.0",
4849
"eslint-plugin-unused-imports": "^2.0.0",

src/app/error.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @next/next/no-img-element */
12
'use client'; // Error components must be Client Components
23

34
import WarningIcon from '@mui/icons-material/Warning';
@@ -29,6 +30,12 @@ export default function Error({
2930
<Button onClick={reset}>Try again</Button>
3031
</Box>
3132
<a href='/?slug=homepage'>Back to home</a>
33+
<div>
34+
<img
35+
src='https://img.freepik.com/free-vector/500-internal-server-error-concept-illustration_114360-1905.jpg'
36+
alt='500'
37+
/>
38+
</div>
3239
</Box>
3340
</section>
3441
</main>

src/app/not-found.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
/* eslint-disable @next/next/no-img-element */
2+
'use client';
3+
14
import { Box } from '@mui/material';
25
import { Metadata } from 'next';
6+
import { usePathname } from 'next/navigation';
37
import * as React from 'react';
48
import { RiAlarmWarningFill } from 'react-icons/ri';
59

@@ -8,6 +12,7 @@ export const metadata: Metadata = {
812
};
913

1014
export default function NotFound() {
15+
const pathname = usePathname();
1116
return (
1217
<main>
1318
<Box sx={{ textAlign: 'center' }}>
@@ -17,8 +22,16 @@ export default function NotFound() {
1722
className='drop-shadow-glow animate-flicker text-red-500'
1823
/>
1924
<h1>Page Not Found</h1>
25+
{/* <div>{window.location.href} NOT exists</div> */}
26+
<div>{pathname} NOT exists</div>
2027
<h5>change this in app/not-found.tsx</h5>
2128
<a href='/'>Back to home</a>
29+
<div>
30+
<img
31+
src='https://img.freepik.com/free-vector/404-error-with-person-looking-concept-illustration_114360-7922.jpg'
32+
alt='404'
33+
/>
34+
</div>
2235
</div>
2336
</Box>
2437
</main>

src/app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { NpmData, PageParams } from '@/types';
66

77
const loadDataFromApi = async (slug?: string) => {
88
if (slug === 'testError500') {
9-
throw new Error('This is mock a ssr 500 test error');
9+
throw new Error('This is mock a SSR 500 test error');
1010
}
1111

1212
// Fetch & cache data from 2 remote APIs test

src/components/Homepage.tsx

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import AutoAwesome from '@mui/icons-material/AutoAwesome';
22
import { Box, Typography } from '@mui/material';
33
import Link from 'next/link';
44

5-
import { ClientProvider } from '@/hooks/useClientContext';
6-
7-
import DisplayRandomPicture from '@/components/shared/DisplayRandomPicture';
8-
import PageFooter from '@/components/shared/PageFooter';
9-
import ReactActionForm from '@/components/shared/ReactActionForm';
10-
import ReactHookForm from '@/components/shared/ReactHookForm';
5+
import BottomLinks from '@/components/homepage/BottomLinks';
6+
import DisplayRandomPicture from '@/components/homepage/DisplayRandomPicture';
7+
import PageFooter from '@/components/homepage/PageFooter';
8+
import ReactActionForm from '@/components/homepage/ReactActionForm';
9+
import ReactHookForm from '@/components/homepage/ReactHookForm';
10+
import ClientSideWrapper from '@/components/shared/ClientSideWrapper';
1111

1212
import { FETCH_API_CTX_VALUE, SITE_CONFIG } from '@/constants';
1313

@@ -67,39 +67,12 @@ export default function Homepage({
6767
Test local NextJs API /api/test POST method (client-side
6868
component)
6969
</h4>
70-
<ClientProvider defaultValue={FETCH_API_CTX_VALUE}>
70+
<ClientSideWrapper defaultContextValue={FETCH_API_CTX_VALUE}>
7171
<ReactActionForm />
7272
<ReactHookForm />
7373
<DisplayRandomPicture />
74-
</ClientProvider>
75-
</Box>
76-
77-
<Box sx={{ m: 5 }}>
78-
<Link
79-
href='https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter'
80-
target='_blank'
81-
>
82-
See the Github repository page
83-
</Link>
84-
</Box>
85-
<Box sx={{ m: 5, a: { color: 'red' } }}>
86-
<Link
87-
href='https://vercel.com/new/clone?s=https%3A%2F%2Fgithub.com%2FAlexStack%2Fnextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter&showOptionalTeamCreation=false'
88-
target='_blank'
89-
>
90-
Click here to deploy a demo site to your Vercel in 1 minute
91-
</Link>
92-
</Box>
93-
94-
<Box sx={{ m: 5 }}>
95-
<Link href='/test-page-not-exists'>
96-
Test 404 page not found (mock file not exists)
97-
</Link>
98-
</Box>
99-
<Box sx={{ m: 5 }}>
100-
<a href='/?slug=testError500'>
101-
Test 500 error page (mock server side throw error)
102-
</a>
74+
<BottomLinks />
75+
</ClientSideWrapper>
10376
</Box>
10477
</Box>
10578
</section>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
'use client';
2+
3+
import { Box } from '@mui/material';
4+
import Link from 'next/link';
5+
import * as React from 'react';
6+
7+
import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';
8+
9+
const BottomLinks = () => {
10+
const { openConfirmDialog } = useSharedUtilContext();
11+
12+
return (
13+
<section>
14+
<Box sx={{ m: 5 }}>
15+
<Link
16+
href='https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter'
17+
target='_blank'
18+
>
19+
See the Github repository page
20+
</Link>
21+
</Box>
22+
<Box sx={{ m: 5, a: { color: 'red' } }}>
23+
<Link
24+
href='https://vercel.com/new/clone?s=https%3A%2F%2Fgithub.com%2FAlexStack%2Fnextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter&showOptionalTeamCreation=false'
25+
target='_blank'
26+
onClick={(e) => {
27+
e.preventDefault();
28+
openConfirmDialog({
29+
title: 'Copy this repository to your Vercel',
30+
content:
31+
'Please make sure you have a Vercel account and login first',
32+
onConfirm: () => {
33+
window.open((e.target as HTMLAnchorElement).href, '_blank');
34+
},
35+
hideCancelButton: true,
36+
});
37+
}}
38+
>
39+
Click here to deploy a demo site to your Vercel in 1 minute
40+
</Link>
41+
</Box>
42+
43+
<Box sx={{ m: 5 }}>
44+
<Link
45+
href='/test-page-not-exists'
46+
onClick={(e) => {
47+
e.preventDefault();
48+
openConfirmDialog({
49+
title: 'Mock a page not found',
50+
content:
51+
'This is an URL not exists, click OK you will see a custom 404 error page. You can also test the 404 page by typing a random URL in the browser address bar.',
52+
onConfirm: () => {
53+
window.open((e.target as HTMLAnchorElement).href, '_blank');
54+
},
55+
hideCancelButton: true,
56+
});
57+
}}
58+
>
59+
Test 404 page not found (mock file not exists)
60+
</Link>
61+
</Box>
62+
<Box sx={{ m: 5 }}>
63+
<a
64+
href='/?slug=testError500'
65+
onClick={(e) => {
66+
e.preventDefault();
67+
openConfirmDialog({
68+
title: 'Mock a server side error',
69+
content:
70+
'This is mock throw a server side error, click OK you will see a custom 500 error page. ',
71+
onConfirm: () => {
72+
window.open((e.target as HTMLAnchorElement).href, '_blank');
73+
},
74+
});
75+
}}
76+
>
77+
Test 500 error page (mock server side throw error)
78+
</a>
79+
</Box>
80+
</section>
81+
);
82+
};
83+
84+
export default BottomLinks;

src/components/shared/DisplayRandomPicture.tsx renamed to src/components/homepage/DisplayRandomPicture.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,20 @@ import { purple } from '@mui/material/colors';
1010
import Stack from '@mui/material/Stack';
1111
import React, { useEffect, useState, useTransition } from 'react';
1212

13-
import { useAlertBar } from '@/hooks/useAlertBar';
1413
import { useClientContext } from '@/hooks/useClientContext';
14+
import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';
1515

1616
import SubmitButton from '@/components/shared/SubmitButton';
1717

1818
import { FetchApiContext } from '@/constants';
19+
import { consoleLog } from '@/utils/shared/console-log';
1920
import { getApiResponse } from '@/utils/shared/get-api-response';
2021

2122
const DisplayRandomPicture = () => {
2223
const [imageUrl, setImageUrl] = useState('');
2324
const [error, setError] = useState('');
2425
const { fetchCount, updateClientCtx } = useClientContext<FetchApiContext>();
25-
const { setAlertBarProps, renderAlertBar } = useAlertBar();
26+
const { setAlertBarProps } = useSharedUtilContext();
2627
const renderCountRef = React.useRef(0);
2728
const [isPending, startTransition] = useTransition();
2829

@@ -52,6 +53,9 @@ const DisplayRandomPicture = () => {
5253
setAlertBarProps({
5354
message: 'A random picture fetched successfully',
5455
severity: 'info',
56+
onClose: () => {
57+
consoleLog('Alert bar closed');
58+
},
5559
});
5660
} catch (error) {
5761
const errorMsg =
@@ -121,7 +125,6 @@ const DisplayRandomPicture = () => {
121125
</Avatar>
122126
</StyledRefreshButton>
123127
)}
124-
{renderAlertBar()}
125128
</Stack>
126129
);
127130
};

src/components/shared/ReactActionForm.tsx renamed to src/components/homepage/ReactActionForm.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { purple } from '@mui/material/colors';
1414
import React, { useActionState, useOptimistic } from 'react';
1515
import { z, ZodError } from 'zod';
1616

17-
import { useAlertBar } from '@/hooks/useAlertBar';
1817
import { useClientContext } from '@/hooks/useClientContext';
18+
import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';
1919

2020
import SubmitButton from '@/components/shared/SubmitButton';
2121

@@ -51,7 +51,7 @@ const ReactActionForm: React.FC = () => {
5151
FormValues | undefined
5252
>(undefined);
5353

54-
const { setAlertBarProps, renderAlertBar } = useAlertBar();
54+
const { setAlertBarProps } = useSharedUtilContext();
5555

5656
const { fetchCount, updateClientCtx } = useClientContext<FetchApiContext>();
5757
const [formErrors, setFormErrors] = React.useState<Record<string, string>>(
@@ -184,7 +184,7 @@ const ReactActionForm: React.FC = () => {
184184
justifyContent='center'
185185
alignItems='center'
186186
>
187-
<div>Total fetch count from React Context:</div>
187+
<div>Total fetch count from ReactActionForm.tsx:</div>
188188
<Avatar
189189
sx={{
190190
bgcolor: purple[500],
@@ -198,8 +198,6 @@ const ReactActionForm: React.FC = () => {
198198
</Avatar>
199199
</Stack>
200200
</Box>
201-
202-
{renderAlertBar()}
203201
</StyledForm>
204202
);
205203
};

src/components/shared/ReactHookForm.tsx renamed to src/components/homepage/ReactHookForm.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ import React, { useEffect } from 'react';
1616
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
1717
import { z } from 'zod';
1818

19-
import { useAlertBar } from '@/hooks/useAlertBar';
2019
import { useClientContext } from '@/hooks/useClientContext';
21-
import useConfirmationDialog from '@/hooks/useConfirmDialog';
20+
import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';
2221

2322
import SubmitButton from '@/components/shared/SubmitButton';
2423

@@ -52,10 +51,7 @@ const ReactHookForm: React.FC = () => {
5251
const [apiResult, setApiResult] = React.useState<FormValues>();
5352
const [isSubmitting, setIsSubmitting] = React.useState(false);
5453

55-
const { setAlertBarProps, renderAlertBar } = useAlertBar();
56-
57-
const { openConfirmDialog, renderConfirmationDialog } =
58-
useConfirmationDialog();
54+
const { setAlertBarProps, openConfirmDialog } = useSharedUtilContext();
5955

6056
const {
6157
handleSubmit,
@@ -195,10 +191,6 @@ const ReactHookForm: React.FC = () => {
195191
Test MUI confirmation dialog
196192
</Button>
197193
</Box>
198-
199-
{renderAlertBar()}
200-
201-
{renderConfirmationDialog()}
202194
</StyledForm>
203195
);
204196
};

0 commit comments

Comments
 (0)