Skip to content

Commit

Permalink
Merge pull request #11 from AlexStack/react19-nextjs15
Browse files Browse the repository at this point in the history
React19 nextjs15
  • Loading branch information
tigbbit committed Aug 10, 2024
2 parents 6557b1f + 48aa05f commit f2e9433
Show file tree
Hide file tree
Showing 18 changed files with 666 additions and 542 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ If you prefer Tailwind css, check this: [Tailwind-CSS-Version](https://github.co

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

- Clone React19-Next15-MUI6-TS-Starter:
- `git clone -b react19-nextjs15 https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter.git react19-nextjs15-mui6-ts-starter`
- Clone React18-Next14-MUI5-TS-Starter:
- `git clone -b nextjs14 https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter.git react18-nextjs14-mui5-ts-starter`
- Clone & install React19-Next15-MUI6-TS-Starter:
- `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`
- Open <http://localhost:3005>
- Note: React 19 is not released yet, there are some warnings in the console, please ignore them.
- Clone & install React18-Next14-MUI5-TS-Starter:
- `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`
- Open <http://localhost:3005>

## Features

Expand Down
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.1",
"@mui/icons-material": "^5.14.9",
"@mui/material": "^5.14.10",
"@mui/icons-material": "next",
"@mui/material": "next",
"dayjs": "^1.11.10",
"next": "^15.0.0-rc.0",
"react": "^19.0.0-rc-512b09b2-20240718",
"react-dom": "^19.0.0-rc-512b09b2-20240718",
"react-hook-form": "^7.46.2",
"next": "rc",
"react": "rc",
"react-dom": "rc",
"react-hook-form": "^7.52.2",
"react-icons": "^4.10.1",
"zod": "^3.22.4"
},
Expand All @@ -38,11 +38,12 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@types/react": "^18.2.15",
"@types/jest": "^29.5.12",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"autoprefixer": "^10.4.14",
"eslint": "^8.45.0",
"eslint-config-next": "^15.0.0-rc.0",
"eslint-config-next": "rc",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
Expand Down
7 changes: 7 additions & 0 deletions src/app/error.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @next/next/no-img-element */
'use client'; // Error components must be Client Components

import WarningIcon from '@mui/icons-material/Warning';
Expand Down Expand Up @@ -29,6 +30,12 @@ export default function Error({
<Button onClick={reset}>Try again</Button>
</Box>
<a href='/?slug=homepage'>Back to home</a>
<div>
<img
src='https://img.freepik.com/free-vector/500-internal-server-error-concept-illustration_114360-1905.jpg'
alt='500'
/>
</div>
</Box>
</section>
</main>
Expand Down
13 changes: 13 additions & 0 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* eslint-disable @next/next/no-img-element */
'use client';

import { Box } from '@mui/material';
import { Metadata } from 'next';
import { usePathname } from 'next/navigation';
import * as React from 'react';
import { RiAlarmWarningFill } from 'react-icons/ri';

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

export default function NotFound() {
const pathname = usePathname();
return (
<main>
<Box sx={{ textAlign: 'center' }}>
Expand All @@ -17,8 +22,16 @@ export default function NotFound() {
className='drop-shadow-glow animate-flicker text-red-500'
/>
<h1>Page Not Found</h1>
{/* <div>{window.location.href} NOT exists</div> */}
<div>{pathname} NOT exists</div>
<h5>change this in app/not-found.tsx</h5>
<a href='/'>Back to home</a>
<div>
<img
src='https://img.freepik.com/free-vector/404-error-with-person-looking-concept-illustration_114360-7922.jpg'
alt='404'
/>
</div>
</div>
</Box>
</main>
Expand Down
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { NpmData, PageParams } from '@/types';

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

// Fetch & cache data from 2 remote APIs test
Expand Down
45 changes: 9 additions & 36 deletions src/components/Homepage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import AutoAwesome from '@mui/icons-material/AutoAwesome';
import { Box, Typography } from '@mui/material';
import Link from 'next/link';

import { ClientProvider } from '@/hooks/useClientContext';

import DisplayRandomPicture from '@/components/shared/DisplayRandomPicture';
import PageFooter from '@/components/shared/PageFooter';
import ReactActionForm from '@/components/shared/ReactActionForm';
import ReactHookForm from '@/components/shared/ReactHookForm';
import BottomLinks from '@/components/homepage/BottomLinks';
import DisplayRandomPicture from '@/components/homepage/DisplayRandomPicture';
import PageFooter from '@/components/homepage/PageFooter';
import ReactActionForm from '@/components/homepage/ReactActionForm';
import ReactHookForm from '@/components/homepage/ReactHookForm';
import ClientSideWrapper from '@/components/shared/ClientSideWrapper';

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

Expand Down Expand Up @@ -67,39 +67,12 @@ export default function Homepage({
Test local NextJs API /api/test POST method (client-side
component)
</h4>
<ClientProvider defaultValue={FETCH_API_CTX_VALUE}>
<ClientSideWrapper defaultContextValue={FETCH_API_CTX_VALUE}>
<ReactActionForm />
<ReactHookForm />
<DisplayRandomPicture />
</ClientProvider>
</Box>

<Box sx={{ m: 5 }}>
<Link
href='https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter'
target='_blank'
>
See the Github repository page
</Link>
</Box>
<Box sx={{ m: 5, a: { color: 'red' } }}>
<Link
href='https://vercel.com/new/clone?s=https%3A%2F%2Fgithub.com%2FAlexStack%2Fnextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter&showOptionalTeamCreation=false'
target='_blank'
>
Click here to deploy a demo site to your Vercel in 1 minute
</Link>
</Box>

<Box sx={{ m: 5 }}>
<Link href='/test-page-not-exists'>
Test 404 page not found (mock file not exists)
</Link>
</Box>
<Box sx={{ m: 5 }}>
<a href='/?slug=testError500'>
Test 500 error page (mock server side throw error)
</a>
<BottomLinks />
</ClientSideWrapper>
</Box>
</Box>
</section>
Expand Down
84 changes: 84 additions & 0 deletions src/components/homepage/BottomLinks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'use client';

import { Box } from '@mui/material';
import Link from 'next/link';
import * as React from 'react';

import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';

const BottomLinks = () => {
const { openConfirmDialog } = useSharedUtilContext();

return (
<section>
<Box sx={{ m: 5 }}>
<Link
href='https://github.com/AlexStack/nextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter'
target='_blank'
>
See the Github repository page
</Link>
</Box>
<Box sx={{ m: 5, a: { color: 'red' } }}>
<Link
href='https://vercel.com/new/clone?s=https%3A%2F%2Fgithub.com%2FAlexStack%2Fnextjs-materia-mui-typescript-hook-form-scaffold-boilerplate-starter&showOptionalTeamCreation=false'
target='_blank'
onClick={(e) => {
e.preventDefault();
openConfirmDialog({
title: 'Copy this repository to your Vercel',
content:
'Please make sure you have a Vercel account and login first',
onConfirm: () => {
window.open((e.target as HTMLAnchorElement).href, '_blank');
},
hideCancelButton: true,
});
}}
>
Click here to deploy a demo site to your Vercel in 1 minute
</Link>
</Box>

<Box sx={{ m: 5 }}>
<Link
href='/test-page-not-exists'
onClick={(e) => {
e.preventDefault();
openConfirmDialog({
title: 'Mock a page not found',
content:
'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.',
onConfirm: () => {
window.open((e.target as HTMLAnchorElement).href, '_blank');
},
hideCancelButton: true,
});
}}
>
Test 404 page not found (mock file not exists)
</Link>
</Box>
<Box sx={{ m: 5 }}>
<a
href='/?slug=testError500'
onClick={(e) => {
e.preventDefault();
openConfirmDialog({
title: 'Mock a server side error',
content:
'This is mock throw a server side error, click OK you will see a custom 500 error page. ',
onConfirm: () => {
window.open((e.target as HTMLAnchorElement).href, '_blank');
},
});
}}
>
Test 500 error page (mock server side throw error)
</a>
</Box>
</section>
);
};

export default BottomLinks;
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import { purple } from '@mui/material/colors';
import Stack from '@mui/material/Stack';
import React, { useEffect, useState, useTransition } from 'react';

import { useAlertBar } from '@/hooks/useAlertBar';
import { useClientContext } from '@/hooks/useClientContext';
import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';

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

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

const DisplayRandomPicture = () => {
const [imageUrl, setImageUrl] = useState('');
const [error, setError] = useState('');
const { fetchCount, updateClientCtx } = useClientContext<FetchApiContext>();
const { setAlertBarProps, renderAlertBar } = useAlertBar();
const { setAlertBarProps } = useSharedUtilContext();
const renderCountRef = React.useRef(0);
const [isPending, startTransition] = useTransition();

Expand Down Expand Up @@ -52,6 +53,9 @@ const DisplayRandomPicture = () => {
setAlertBarProps({
message: 'A random picture fetched successfully',
severity: 'info',
onClose: () => {
consoleLog('Alert bar closed');
},
});
} catch (error) {
const errorMsg =
Expand Down Expand Up @@ -121,7 +125,6 @@ const DisplayRandomPicture = () => {
</Avatar>
</StyledRefreshButton>
)}
{renderAlertBar()}
</Stack>
);
};
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { purple } from '@mui/material/colors';
import React, { useActionState, useOptimistic } from 'react';
import { z, ZodError } from 'zod';

import { useAlertBar } from '@/hooks/useAlertBar';
import { useClientContext } from '@/hooks/useClientContext';
import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';

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

Expand Down Expand Up @@ -51,7 +51,7 @@ const ReactActionForm: React.FC = () => {
FormValues | undefined
>(undefined);

const { setAlertBarProps, renderAlertBar } = useAlertBar();
const { setAlertBarProps } = useSharedUtilContext();

const { fetchCount, updateClientCtx } = useClientContext<FetchApiContext>();
const [formErrors, setFormErrors] = React.useState<Record<string, string>>(
Expand Down Expand Up @@ -184,7 +184,7 @@ const ReactActionForm: React.FC = () => {
justifyContent='center'
alignItems='center'
>
<div>Total fetch count from React Context:</div>
<div>Total fetch count from ReactActionForm.tsx:</div>
<Avatar
sx={{
bgcolor: purple[500],
Expand All @@ -198,8 +198,6 @@ const ReactActionForm: React.FC = () => {
</Avatar>
</Stack>
</Box>

{renderAlertBar()}
</StyledForm>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ import React, { useEffect } from 'react';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import { z } from 'zod';

import { useAlertBar } from '@/hooks/useAlertBar';
import { useClientContext } from '@/hooks/useClientContext';
import useConfirmationDialog from '@/hooks/useConfirmDialog';
import { useSharedUtilContext } from '@/hooks/useSharedUtilContext';

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

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

const { setAlertBarProps, renderAlertBar } = useAlertBar();

const { openConfirmDialog, renderConfirmationDialog } =
useConfirmationDialog();
const { setAlertBarProps, openConfirmDialog } = useSharedUtilContext();

const {
handleSubmit,
Expand Down Expand Up @@ -195,10 +191,6 @@ const ReactHookForm: React.FC = () => {
Test MUI confirmation dialog
</Button>
</Box>

{renderAlertBar()}

{renderConfirmationDialog()}
</StyledForm>
);
};
Expand Down
Loading

0 comments on commit f2e9433

Please sign in to comment.