From 59fa4276defe260bb92dc14b4bcac2023bd897dc Mon Sep 17 00:00:00 2001 From: denchiklut Date: Sun, 29 Sep 2024 13:05:55 -0700 Subject: [PATCH] feat: react-cookie --- @types/app/index.d.ts | 27 ++++++------ package.json | 2 + src/client/components/@shared/app/app.tsx | 9 ++-- .../components/home/cookie-demo/index.tsx | 21 ++++++++++ .../components/home/cookie-demo/styles.scss | 4 ++ src/client/components/home/index.tsx | 5 ++- src/client/components/home/posts/index.tsx | 13 +++--- src/client/components/home/state/index.tsx | 9 +++- src/common/types/index.ts | 2 + src/server/index.ts | 3 +- src/server/middleware/cookie/index.ts | 3 ++ src/server/middleware/index.ts | 1 + src/server/middleware/render/index.tsx | 2 +- yarn.lock | 41 ++++++++++++++++++- 14 files changed, 113 insertions(+), 29 deletions(-) create mode 100644 src/client/components/home/cookie-demo/index.tsx create mode 100644 src/client/components/home/cookie-demo/styles.scss create mode 100644 src/server/middleware/cookie/index.ts diff --git a/@types/app/index.d.ts b/@types/app/index.d.ts index 1d74d50..31c91f3 100644 --- a/@types/app/index.d.ts +++ b/@types/app/index.d.ts @@ -1,19 +1,18 @@ -/** - * If you have `imports/exports` statements - * You need put `Window` & `Express` declarations into `declare global {}` - * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html - */ +import type Cookies from "universal-cookie" -interface Window { - nonce: string -} - -namespace Express { - interface Response { - renderApp(): Promise +declare global { + interface Window { + nonce: string } - interface Request { - nonce: string + namespace Express { + interface Response { + renderApp(): Promise + } + + interface Request { + nonce: string + universalCookies: Cookies + } } } diff --git a/package.json b/package.json index 018820d..7239477 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,13 @@ "morgan": "^1.10.0", "postcss": "^8.4.47", "react": "^18.3.1", + "react-cookie": "^7.2.0", "react-dom": "^18.3.1", "react-helmet-async": "^2.0.5", "react-router": "^6.26.2", "react-router-dom": "^6.26.2", "reflect-metadata": "^0.2.2", + "universal-cookie-express": "^7.2.0", "url-join": "^5.0.0", "uuid": "^10.0.0", "winston": "^3.14.2", diff --git a/src/client/components/@shared/app/app.tsx b/src/client/components/@shared/app/app.tsx index c26ed29..1211e79 100644 --- a/src/client/components/@shared/app/app.tsx +++ b/src/client/components/@shared/app/app.tsx @@ -1,14 +1,17 @@ import { type FC, StrictMode } from 'react' -import { getENV, type AppProps } from 'src/common' +import { CookiesProvider } from 'react-cookie' import { HelmetProvider } from 'react-helmet-async' +import { getENV, type AppProps } from 'src/common' -export const App: FC = ({ children, nonce, helmetContext }) => { +export const App: FC = ({ children, nonce, cookies, helmetContext }) => { __webpack_nonce__ = nonce __webpack_public_path__ = getENV('CLIENT_PUBLIC_PATH') return ( - {children} + + {children} + ) } diff --git a/src/client/components/home/cookie-demo/index.tsx b/src/client/components/home/cookie-demo/index.tsx new file mode 100644 index 0000000..bc04f96 --- /dev/null +++ b/src/client/components/home/cookie-demo/index.tsx @@ -0,0 +1,21 @@ +import { useCookies } from 'react-cookie' +import css from './styles.scss' + +export const CookieDemo = () => { + const [cookies, setCookie] = useCookies() + + return ( +
+ Cookie demo + + + {!cookies.hide && ( +

+ Reload the page! +
+ There will be no hydration errors since its sync between client/server +

+ )} +
+ ) +} diff --git a/src/client/components/home/cookie-demo/styles.scss b/src/client/components/home/cookie-demo/styles.scss new file mode 100644 index 0000000..264670a --- /dev/null +++ b/src/client/components/home/cookie-demo/styles.scss @@ -0,0 +1,4 @@ +.wrapper { + display: grid; + justify-items: start; +} diff --git a/src/client/components/home/index.tsx b/src/client/components/home/index.tsx index 3762583..76b0cf7 100644 --- a/src/client/components/home/index.tsx +++ b/src/client/components/home/index.tsx @@ -1,5 +1,6 @@ import { Suspense } from 'react' import { logger, getENV } from 'src/common' +import { CookieDemo } from './cookie-demo' import { Posts } from './posts' import { State } from './state' import css from './styles.scss' @@ -12,7 +13,9 @@ export const Home = () => {

Home page!

- +
+ +
fetching posts...

}>
diff --git a/src/client/components/home/posts/index.tsx b/src/client/components/home/posts/index.tsx index 743ede1..14afb6c 100644 --- a/src/client/components/home/posts/index.tsx +++ b/src/client/components/home/posts/index.tsx @@ -5,10 +5,13 @@ export const Posts = () => { const posts = useLoaderData() as PostsResponse return ( -
    - {posts.map(post => ( -
  • {post.text}
  • - ))} -
+
+ Data fetching demo +
    + {posts.map(post => ( +
  • {post.text}
  • + ))} +
+
) } diff --git a/src/client/components/home/state/index.tsx b/src/client/components/home/state/index.tsx index 808dedc..1e553d5 100644 --- a/src/client/components/home/state/index.tsx +++ b/src/client/components/home/state/index.tsx @@ -4,9 +4,14 @@ export const State = () => { const [count, setCount] = useState(0) return ( - <> +
+ HMR Demo +

+ Click Increase button and update the code. You selected state will be preserved with + HMR update. +

count {count}

- +
) } diff --git a/src/common/types/index.ts b/src/common/types/index.ts index 6b9e2f8..14a819b 100644 --- a/src/common/types/index.ts +++ b/src/common/types/index.ts @@ -1,8 +1,10 @@ import type { ReactNode } from 'react' import type { HelmetServerState } from 'react-helmet-async' +import type Cookies from 'universal-cookie' export interface AppProps { nonce: string + cookies?: Cookies children: ReactNode helmetContext?: { helmet?: HelmetServerState } } diff --git a/src/server/index.ts b/src/server/index.ts index 9cfc997..fd6e8b5 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,9 +1,10 @@ import express from 'express' -import { favicon, hmr, render, logger, error, uuid } from 'server/middleware' +import { favicon, hmr, render, logger, cookieParser, uuid, error } from 'server/middleware' import { bootstrap } from 'server/utils' import { router } from 'server/router' export const app = express() + .use(cookieParser) .use(favicon()) .use(uuid) .use(logger) diff --git a/src/server/middleware/cookie/index.ts b/src/server/middleware/cookie/index.ts new file mode 100644 index 0000000..37feeff --- /dev/null +++ b/src/server/middleware/cookie/index.ts @@ -0,0 +1,3 @@ +import cookieParserMiddleware from 'universal-cookie-express' + +export const cookieParser = cookieParserMiddleware() diff --git a/src/server/middleware/index.ts b/src/server/middleware/index.ts index 9587d72..941701c 100644 --- a/src/server/middleware/index.ts +++ b/src/server/middleware/index.ts @@ -2,6 +2,7 @@ export * from './hmr' export * from './error' export * from './render' export * from './favicon' +export * from './cookie' export * from './logger' export * from './uuid' export * from './pwa' diff --git a/src/server/middleware/render/index.tsx b/src/server/middleware/render/index.tsx index ed286aa..48510ef 100644 --- a/src/server/middleware/render/index.tsx +++ b/src/server/middleware/render/index.tsx @@ -31,7 +31,7 @@ export const render = (req: Request, res: Response, next: NextFunction) => { context.basename = basename const jsx = chunkExtractor.collectChunks( - +