From e54bff7385c2907642f621368639ba99d2f50414 Mon Sep 17 00:00:00 2001 From: Muhammad Date: Fri, 31 Jan 2025 20:31:52 +0700 Subject: [PATCH] add example --- examples/redux/.gitignore | 2 + examples/redux/assets/logo.svg | 36 +++++++++ examples/redux/components/Counter.tsx | 14 ++++ examples/redux/components/Link.tsx | 15 ++++ examples/redux/global.d.ts | 12 +++ examples/redux/layouts/HeadDefault.tsx | 14 ++++ examples/redux/layouts/LayoutDefault.tsx | 75 +++++++++++++++++++ examples/redux/layouts/style.css | 29 +++++++ .../lib/features/counter/counterSlice.ts | 39 ++++++++++ examples/redux/lib/hooks.ts | 8 ++ examples/redux/package.json | 22 ++++++ examples/redux/pages/+config.ts | 15 ++++ examples/redux/pages/+redux.ts | 22 ++++++ examples/redux/pages/about/+Page.tsx | 12 +++ examples/redux/pages/index/+Page.tsx | 17 +++++ examples/redux/readme.md | 8 ++ examples/redux/tsconfig.json | 13 ++++ examples/redux/vite.config.ts | 7 ++ pnpm-lock.yaml | 39 ++++++++++ 19 files changed, 399 insertions(+) create mode 100644 examples/redux/.gitignore create mode 100644 examples/redux/assets/logo.svg create mode 100644 examples/redux/components/Counter.tsx create mode 100644 examples/redux/components/Link.tsx create mode 100644 examples/redux/global.d.ts create mode 100644 examples/redux/layouts/HeadDefault.tsx create mode 100644 examples/redux/layouts/LayoutDefault.tsx create mode 100644 examples/redux/layouts/style.css create mode 100644 examples/redux/lib/features/counter/counterSlice.ts create mode 100644 examples/redux/lib/hooks.ts create mode 100644 examples/redux/package.json create mode 100644 examples/redux/pages/+config.ts create mode 100644 examples/redux/pages/+redux.ts create mode 100644 examples/redux/pages/about/+Page.tsx create mode 100644 examples/redux/pages/index/+Page.tsx create mode 100644 examples/redux/readme.md create mode 100644 examples/redux/tsconfig.json create mode 100644 examples/redux/vite.config.ts diff --git a/examples/redux/.gitignore b/examples/redux/.gitignore new file mode 100644 index 00000000..b0a5c349 --- /dev/null +++ b/examples/redux/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/dist/ diff --git a/examples/redux/assets/logo.svg b/examples/redux/assets/logo.svg new file mode 100644 index 00000000..94d3caa0 --- /dev/null +++ b/examples/redux/assets/logo.svg @@ -0,0 +1,36 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/redux/components/Counter.tsx b/examples/redux/components/Counter.tsx new file mode 100644 index 00000000..6e0a0a8c --- /dev/null +++ b/examples/redux/components/Counter.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import { useAppDispatch, useAppSelector } from "../lib/hooks"; +import { increment, selectCount } from "../lib/features/counter/counterSlice"; + +export function Counter() { + const dispatch = useAppDispatch() + const count = useAppSelector(selectCount) + + return ( + + ); +} diff --git a/examples/redux/components/Link.tsx b/examples/redux/components/Link.tsx new file mode 100644 index 00000000..3bb18237 --- /dev/null +++ b/examples/redux/components/Link.tsx @@ -0,0 +1,15 @@ +export { Link } + +import { usePageContext } from 'vike-react/usePageContext' +import React from 'react' + +function Link({ href, children }: { href: string; children: string }) { + const pageContext = usePageContext() + const { urlPathname } = pageContext + const isActive = href === '/' ? urlPathname === href : urlPathname.startsWith(href) + return ( + + {children} + + ) +} diff --git a/examples/redux/global.d.ts b/examples/redux/global.d.ts new file mode 100644 index 00000000..7b5a8536 --- /dev/null +++ b/examples/redux/global.d.ts @@ -0,0 +1,12 @@ +import { AppStore, RootState } from './pages/+redux' + +declare global { + namespace Vike { + interface PageContext { + reduxState?: RootState + reduxStore?: AppStore + } + } +} + +export {} diff --git a/examples/redux/layouts/HeadDefault.tsx b/examples/redux/layouts/HeadDefault.tsx new file mode 100644 index 00000000..ef374b88 --- /dev/null +++ b/examples/redux/layouts/HeadDefault.tsx @@ -0,0 +1,14 @@ +export default HeadDefault + +import React from 'react' +import logoUrl from '../assets/logo.svg' + +function HeadDefault() { + return ( + <> + + + + + ) +} diff --git a/examples/redux/layouts/LayoutDefault.tsx b/examples/redux/layouts/LayoutDefault.tsx new file mode 100644 index 00000000..f77d4cec --- /dev/null +++ b/examples/redux/layouts/LayoutDefault.tsx @@ -0,0 +1,75 @@ +export default LayoutDefault + +import './style.css' +import React from 'react' +import logoUrl from '../assets/logo.svg' +import { Link } from '../components/Link' + +function LayoutDefault({ children }: { children: React.ReactNode }) { + return ( +
+ + + Welcome + About + + {children} +
+ ) +} + +function Sidebar({ children }: { children: React.ReactNode }) { + return ( + + ) +} + +function Content({ children }: { children: React.ReactNode }) { + return ( +
+
+ {children} +
+
+ ) +} + +function Logo() { + return ( +
+ + + +
+ ) +} diff --git a/examples/redux/layouts/style.css b/examples/redux/layouts/style.css new file mode 100644 index 00000000..7afa4ca5 --- /dev/null +++ b/examples/redux/layouts/style.css @@ -0,0 +1,29 @@ +/* Links */ +a { + text-decoration: none; +} +#sidebar a { + padding: 2px 10px; + margin-left: -10px; +} +#sidebar a.is-active { + background-color: #eee; +} + +/* Reset */ +body { + margin: 0; + font-family: sans-serif; +} +* { + box-sizing: border-box; +} + +/* Page Transition Anmiation */ +#page-content { + opacity: 1; + transition: opacity 0.3s ease-in-out; +} +body.page-is-transitioning #page-content { + opacity: 0; +} diff --git a/examples/redux/lib/features/counter/counterSlice.ts b/examples/redux/lib/features/counter/counterSlice.ts new file mode 100644 index 00000000..28fe3f13 --- /dev/null +++ b/examples/redux/lib/features/counter/counterSlice.ts @@ -0,0 +1,39 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +export interface CounterSliceState { + value: number; +} + +const initialState: CounterSliceState = { + value: 0, +}; + +export const counterSlice = createSlice({ + name: "counter", + initialState, + reducers: { + increment: (state) => { + state.value += 1; + }, + decrement: (state) => { + state.value -= 1; + }, + incrementByAmount: (state, action: PayloadAction) => { + state.value += action.payload; + }, + initializeCount: (state, action: PayloadAction) => { + state.value = action.payload; + }, + }, + selectors: { + selectCount: (counter) => counter.value, + }, +}); + +export const { selectCount } = counterSlice.selectors; + +export const { increment, decrement, incrementByAmount, initializeCount } = + counterSlice.actions; + +export default counterSlice.reducer; diff --git a/examples/redux/lib/hooks.ts b/examples/redux/lib/hooks.ts new file mode 100644 index 00000000..bb96bf7a --- /dev/null +++ b/examples/redux/lib/hooks.ts @@ -0,0 +1,8 @@ +// This file serves as a central hub for re-exporting pre-typed Redux hooks. +import { useDispatch, useSelector, useStore } from "react-redux"; +import type { AppDispatch, AppStore, RootState } from "../pages/+redux"; + +// Use throughout your app instead of plain `useDispatch` and `useSelector` +export const useAppDispatch = useDispatch.withTypes(); +export const useAppSelector = useSelector.withTypes(); +export const useAppStore = useStore.withTypes(); diff --git a/examples/redux/package.json b/examples/redux/package.json new file mode 100644 index 00000000..c8a38a11 --- /dev/null +++ b/examples/redux/package.json @@ -0,0 +1,22 @@ +{ + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite build && vite preview" + }, + "dependencies": { + "@reduxjs/toolkit": "^2.5.0", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "@vitejs/plugin-react": "^4.2.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-redux": "^9.2.0", + "typescript": "^5.3.3", + "vike": "^0.4.211", + "vike-react": "^0.5.12", + "vike-react-redux": "^0.0.0", + "vite": "^5.4.0" + }, + "type": "module" +} \ No newline at end of file diff --git a/examples/redux/pages/+config.ts b/examples/redux/pages/+config.ts new file mode 100644 index 00000000..70069c19 --- /dev/null +++ b/examples/redux/pages/+config.ts @@ -0,0 +1,15 @@ +import type { Config } from 'vike/types' +import Layout from '../layouts/LayoutDefault' +import Head from '../layouts/HeadDefault' +import vikeReact from 'vike-react/config' +import vikeReactRedux from 'vike-react-redux/config' + +// Default configs (can be overridden by pages) +export default { + Layout, + Head, + // + title: 'My Vike + React App', + extends: [vikeReact, vikeReactRedux], + passToClient: ['routeParams'], +} satisfies Config diff --git a/examples/redux/pages/+redux.ts b/examples/redux/pages/+redux.ts new file mode 100644 index 00000000..cedc2b02 --- /dev/null +++ b/examples/redux/pages/+redux.ts @@ -0,0 +1,22 @@ +export { redux } + +import { combineReducers, configureStore } from '@reduxjs/toolkit' +import counterReducer from '../lib/features/counter/counterSlice' + +const rootReducer = combineReducers({ counter: counterReducer }) + +const redux = { + createStore: (preloadedState: any) => { + return configureStore({ + reducer: rootReducer, + preloadedState, + }) + }, +} + +// Infer the type of createStore +export type AppStore = ReturnType<typeof redux.createStore> +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType<AppStore['getState']> +// Infer the `AppDispatch` type from the store itself +export type AppDispatch = AppStore['dispatch'] diff --git a/examples/redux/pages/about/+Page.tsx b/examples/redux/pages/about/+Page.tsx new file mode 100644 index 00000000..32518f4f --- /dev/null +++ b/examples/redux/pages/about/+Page.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import { Counter } from "../../components/Counter"; + +export default function Page() { + return ( + <> + <h1>About</h1> + <p>The counter value is the same as on the Welcome page.</p> + <Counter /> + </> + ); +} diff --git a/examples/redux/pages/index/+Page.tsx b/examples/redux/pages/index/+Page.tsx new file mode 100644 index 00000000..cb1613cb --- /dev/null +++ b/examples/redux/pages/index/+Page.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { Counter } from "../../components/Counter"; + +export default function Page() { + return ( + <> + <h1>My Vike app</h1> + This page is: + <ul> + <li>Rendered to HTML.</li> + <li> + Interactive. <Counter /> + </li> + </ul> + </> + ); +} diff --git a/examples/redux/readme.md b/examples/redux/readme.md new file mode 100644 index 00000000..cfcd6f09 --- /dev/null +++ b/examples/redux/readme.md @@ -0,0 +1,8 @@ +Example of using `vike-react-redux`. + +```bash +git clone git@github.com:vikejs/vike-react +cd vike-react/examples/redux/ +npm install +npm run dev +``` diff --git a/examples/redux/tsconfig.json b/examples/redux/tsconfig.json new file mode 100644 index 00000000..e0bb64ac --- /dev/null +++ b/examples/redux/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2020", + "moduleResolution": "Node", + "target": "ES2020", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "types": ["vite/client"], + "jsx": "react", + "skipLibCheck": true, + "esModuleInterop": true + } +} diff --git a/examples/redux/vite.config.ts b/examples/redux/vite.config.ts new file mode 100644 index 00000000..aa36b02f --- /dev/null +++ b/examples/redux/vite.config.ts @@ -0,0 +1,7 @@ +import react from '@vitejs/plugin-react' +import vike from 'vike/plugin' +import { UserConfig } from 'vite' + +export default { + plugins: [react(), vike()], +} satisfies UserConfig diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9a2a8bd1..b32f8c46 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -169,6 +169,45 @@ importers: specifier: ^5.4.0 version: 5.4.0(@types/node@20.11.17) + examples/redux: + dependencies: + '@reduxjs/toolkit': + specifier: ^2.5.0 + version: 2.5.0(react-redux@9.2.0(@types/react@18.3.3)(react@18.3.1)(redux@5.0.1))(react@18.3.1) + '@types/react': + specifier: ^18.2.55 + version: 18.3.3 + '@types/react-dom': + specifier: ^18.2.19 + version: 18.3.0 + '@vitejs/plugin-react': + specifier: ^4.2.1 + version: 4.3.1(vite@5.4.0(@types/node@20.11.17)) + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-redux: + specifier: ^9.2.0 + version: 9.2.0(@types/react@18.3.3)(react@18.3.1)(redux@5.0.1) + typescript: + specifier: ^5.3.3 + version: 5.5.4 + vike: + specifier: ^0.4.211 + version: 0.4.211(react-streaming@0.3.47(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(vite@5.4.0(@types/node@20.11.17)) + vike-react: + specifier: link:../../packages/vike-react + version: link:../../packages/vike-react + vike-react-redux: + specifier: link:../../packages/vike-react-redux + version: link:../../packages/vike-react-redux + vite: + specifier: ^5.4.0 + version: 5.4.0(@types/node@20.11.17) + packages/vike-react: dependencies: react-streaming: