From 09f3b9e10060f582d7ee7c0eec025d459f440b07 Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 16:15:53 +0200 Subject: [PATCH 01/11] chore: install redux packages Install the @reduxjs/toolkit and eact-redux Redux packages as dev dependencies for its future integration in Next.js --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index f082ba1..3687d23 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "devDependencies": { "@commitlint/cli": "^21.0.1", "@commitlint/config-conventional": "^21.0.1", + "@reduxjs/toolkit": "^2.12.0", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", @@ -38,6 +39,7 @@ "eslint": "^9", "eslint-config-next": "16.0.3", "husky": "^9.1.7", + "react-redux": "^9.3.0", "tailwindcss": "^4", "tw-animate-css": "^1.4.0", "typescript": "^5" From 0f21c70492bb368cb88557ad990303e17954a50e Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 19:09:45 +0200 Subject: [PATCH 02/11] chore(lib): configure Redux store Following the official documentation 'Redux Toolkit Setup with Next.js' https://redux-toolkit.js.org/usage/nextjs --- app/store-provider.tsx | 0 hooks/use-app-dispatch.ts | 0 hooks/use-app-selector.ts | 0 hooks/use-app-store.ts | 0 lib/store.ts | 17 +++++++++++++++++ 5 files changed, 17 insertions(+) create mode 100644 app/store-provider.tsx create mode 100644 hooks/use-app-dispatch.ts create mode 100644 hooks/use-app-selector.ts create mode 100644 hooks/use-app-store.ts create mode 100644 lib/store.ts diff --git a/app/store-provider.tsx b/app/store-provider.tsx new file mode 100644 index 0000000..e69de29 diff --git a/hooks/use-app-dispatch.ts b/hooks/use-app-dispatch.ts new file mode 100644 index 0000000..e69de29 diff --git a/hooks/use-app-selector.ts b/hooks/use-app-selector.ts new file mode 100644 index 0000000..e69de29 diff --git a/hooks/use-app-store.ts b/hooks/use-app-store.ts new file mode 100644 index 0000000..e69de29 diff --git a/lib/store.ts b/lib/store.ts new file mode 100644 index 0000000..d2ba46b --- /dev/null +++ b/lib/store.ts @@ -0,0 +1,17 @@ +import {configureStore} from "@reduxjs/toolkit"; + +/** +* Function for creating a new store instance per-request (instead of global singleton) +* to avoid data contamination from different requests. +* */ +export const makeStore = () => { + return configureStore({ + reducer: {}, + }) +} + +// Infer the type of makeStore +export type AppStore = ReturnType; +// Infer the `RootState` and `AppDispatch` types from the store itself +export type RootState = ReturnType; +export type AppDispatch = AppStore['dispatch']; \ No newline at end of file From aad6dbba7abfbf6a3994d75511450a175f2f0ac4 Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 19:14:42 +0200 Subject: [PATCH 03/11] chore(hooks): configure Redux hooks Configure with types useAppDispatch, useAppSelector, and useAppStor following the official documentation 'Redux Toolkit Setup with Next.js' https://redux-toolkit.js.org/usage/nextjs#caching --- hooks/use-app-dispatch.ts | 4 ++++ hooks/use-app-selector.ts | 4 ++++ hooks/use-app-store.ts | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/hooks/use-app-dispatch.ts b/hooks/use-app-dispatch.ts index e69de29..6bb7c90 100644 --- a/hooks/use-app-dispatch.ts +++ b/hooks/use-app-dispatch.ts @@ -0,0 +1,4 @@ +import {useDispatch} from "react-redux"; +import {AppDispatch} from "@/lib/store"; + +export const useAppDispatch = useDispatch.withTypes(); \ No newline at end of file diff --git a/hooks/use-app-selector.ts b/hooks/use-app-selector.ts index e69de29..3c94a42 100644 --- a/hooks/use-app-selector.ts +++ b/hooks/use-app-selector.ts @@ -0,0 +1,4 @@ +import {useSelector} from "react-redux"; +import {RootState} from "@/lib/store"; + +export const useAppSelector = useSelector.withTypes(); \ No newline at end of file diff --git a/hooks/use-app-store.ts b/hooks/use-app-store.ts index e69de29..8822165 100644 --- a/hooks/use-app-store.ts +++ b/hooks/use-app-store.ts @@ -0,0 +1,4 @@ +import {useStore} from "react-redux"; +import {AppStore} from "@/lib/store"; + +export const useAppStore = useStore.withTypes() \ No newline at end of file From 5e1a7b647306bebdeafe5213b3401c707935e8a1 Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 19:20:46 +0200 Subject: [PATCH 04/11] chore(app): create StoreProvider Create a StoreProvider component following the official Redux Toolkit Setup with Next.js documentation https://redux-toolkit.js.org/usage/nextjs#caching --- app/store-provider.tsx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/store-provider.tsx b/app/store-provider.tsx index e69de29..9e08657 100644 --- a/app/store-provider.tsx +++ b/app/store-provider.tsx @@ -0,0 +1,25 @@ +'use client' +import { useRef } from 'react' +import { Provider } from 'react-redux' +import { makeStore, AppStore } from '../lib/store' + +export default function StoreProvider({ + children, + }: { + children: React.ReactNode +}) { + const storeRef = useRef(undefined); + + // The code is copy-pasted from the official Redux documentation https://redux-toolkit.js.org/usage/nextjs#providing-the-store. + // Disable ESLint to follow the guidelines. + // eslint-disable-next-line + if (!storeRef.current) { + // Create the store instance the first time this renders + storeRef.current = makeStore() + } + + // The code is copy-pasted from the official Redux documentation https://redux-toolkit.js.org/usage/nextjs#providing-the-store. + // Disable ESLint to follow the guidelines. + // eslint-disable-next-line + return {children} +} \ No newline at end of file From 65f911af2a7bc6e16ffb8473ae48321cb5b1318b Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 19:21:23 +0200 Subject: [PATCH 05/11] feat(app/layout): wrap in StoreProvider Provide the Redux store to the main layout of the app --- app/layout.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index 1ade7bd..23d5c71 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,6 +4,7 @@ import type { Metadata } from "next"; import "./globals.css"; /* fonts*/ import { inter } from "@/assets/fonts"; +import StoreProvider from "@/app/store-provider"; export const metadata: Metadata = { title: "Scents", @@ -16,12 +17,14 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - + + + {children} - - + + + ); } From d12fce18dfee3f7681cc6c8776da190f6118f413 Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 21:46:02 +0200 Subject: [PATCH 06/11] feat: redux demo setup - lib/features/counter/counterSlice.ts: configure counter slice with its reducers, selectors and actions; - lib/features/counter/counterAPI.ts: an mock API-request function for demo; - lib/createAppSlice.ts: function for creating slices with async thunks; - lib/store.ts: set the reducer to rootReducer, update RootState to be type of rootReducer - app/api/counter/route.ts: api endpoint for the demo --- app/api/counter/route.ts | 16 +++++++ lib/createAppSlice.ts | 6 +++ lib/features/counter/counterAPI.ts | 11 +++++ lib/features/counter/counterSlice.ts | 71 ++++++++++++++++++++++++++++ lib/store.ts | 21 ++++++-- 5 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 app/api/counter/route.ts create mode 100644 lib/createAppSlice.ts create mode 100644 lib/features/counter/counterAPI.ts create mode 100644 lib/features/counter/counterSlice.ts diff --git a/app/api/counter/route.ts b/app/api/counter/route.ts new file mode 100644 index 0000000..1089228 --- /dev/null +++ b/app/api/counter/route.ts @@ -0,0 +1,16 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +interface Context { + params: undefined; +} + +export async function POST(request: NextRequest, context: Context) { + const body: { amount: number } = await request.json(); + const { amount = 1 } = body; + + // simulate IO latency + await new Promise((resolve) => setTimeout(resolve, 500)); + + return NextResponse.json({ data: amount }); +} \ No newline at end of file diff --git a/lib/createAppSlice.ts b/lib/createAppSlice.ts new file mode 100644 index 0000000..7e681ba --- /dev/null +++ b/lib/createAppSlice.ts @@ -0,0 +1,6 @@ +import { asyncThunkCreator, buildCreateSlice } from "@reduxjs/toolkit"; + +// `buildCreateSlice` allows us to create a slice with async thunks. +export const createAppSlice = buildCreateSlice({ + creators: { asyncThunk: asyncThunkCreator }, +}); \ No newline at end of file diff --git a/lib/features/counter/counterAPI.ts b/lib/features/counter/counterAPI.ts new file mode 100644 index 0000000..0c4a683 --- /dev/null +++ b/lib/features/counter/counterAPI.ts @@ -0,0 +1,11 @@ +// A mock function to mimic making an async request for data +export const fetchCount = async (amount = 1) => { + const response = await fetch("http://localhost:3000/api/counter", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ amount }), + }); + const result: { data: number } = await response.json(); + + return result; +}; \ No newline at end of file diff --git a/lib/features/counter/counterSlice.ts b/lib/features/counter/counterSlice.ts new file mode 100644 index 0000000..8ddf14d --- /dev/null +++ b/lib/features/counter/counterSlice.ts @@ -0,0 +1,71 @@ +import {asyncThunkCreator, PayloadAction} from "@reduxjs/toolkit"; +import {createAppSlice} from "@/lib/createAppSlice"; +import {fetchCount} from "@/lib/features/counter/counterAPI"; +import {AppThunk} from "@/lib/store"; + +export interface CounterSliceState { + value: number; + status: "idle" | "loading" | "failed"; +} + +const initialState: CounterSliceState = { + value: 0, + status: "idle", +}; + +export const counterSlice = createAppSlice({ + name: "counter", + initialState, + reducers: (create) => ({ + increment: create.reducer((state) => { + state.value += 1; + }), + decrement: create.reducer((state) => { + state.value -= 1; + }), + incrementByAmount: create.reducer((state, action: PayloadAction) => { + state.value += action.payload; + }), + incrementAsync: create.asyncThunk( + async (amount: number) => { + const response = await fetchCount(amount); + // The value we return becomes the `fulfilled` action payload + return response.data; + }, + { + pending: (state) => { + state.status = "loading"; + }, + fulfilled: (state, action) => { + state.status = "idle"; + state.value += action.payload; + }, + rejected: (state) => { + state.status = "failed"; + }, + }, + ), + }), + selectors: { + selectCount: (counter) => counter.value, + selectStatus: (counter) => counter.status, + }, +}); + +// Action creators are generated for each case reducer function. +export const { decrement, increment, incrementAsync, incrementByAmount } = counterSlice.actions; + +// Selectors returned by `slice.selectors` take the root state as their first argument. +export const { selectCount, selectStatus} = counterSlice.selectors; + +// We can also write thunks by hand, which may contain both sync and async logic. +// Here's an example of conditionally dispatching actions based on current state. +export const incrementIfOdd = + (amount: number): AppThunk => + (dispatch, getState) => { + const currentValue = selectCount(getState()); + + if (currentValue % 2 === 1 || currentValue % 2 === -1) { + dispatch(incrementByAmount(amount)); + } + }; \ No newline at end of file diff --git a/lib/store.ts b/lib/store.ts index d2ba46b..f9b030a 100644 --- a/lib/store.ts +++ b/lib/store.ts @@ -1,4 +1,9 @@ -import {configureStore} from "@reduxjs/toolkit"; +import {Action, combineSlices, configureStore, ThunkAction} from "@reduxjs/toolkit"; +import {counterSlice} from "@/lib/features/counter/counterSlice"; + +// `combineSlices` automatically combines the reducers using +// their `reducerPath`s, therefore we no longer need to call `combineReducers`. +const rootReducer = combineSlices(counterSlice); /** * Function for creating a new store instance per-request (instead of global singleton) @@ -6,12 +11,18 @@ import {configureStore} from "@reduxjs/toolkit"; * */ export const makeStore = () => { return configureStore({ - reducer: {}, + reducer: rootReducer, }) } // Infer the type of makeStore export type AppStore = ReturnType; -// Infer the `RootState` and `AppDispatch` types from the store itself -export type RootState = ReturnType; -export type AppDispatch = AppStore['dispatch']; \ No newline at end of file +// Infer the `RootState` type from the root reducer +export type RootState = ReturnType; +export type AppDispatch = AppStore['dispatch']; +export type AppThunk = ThunkAction< + ThunkReturnType, + RootState, + unknown, + Action +>; \ No newline at end of file From 310895b9d2e0fa0a2e80fcf444c96f30b42ad3cd Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 21:46:46 +0200 Subject: [PATCH 07/11] feat: redux demo usage - app/demo/page.tsx: redux demo UI - app/demo/demo.module.css: stylings for the redux demo UI --- app/demo/demo.module.css | 81 +++++ app/demo/page.tsx | 719 ++++++++++++++++++++++----------------- 2 files changed, 484 insertions(+), 316 deletions(-) create mode 100644 app/demo/demo.module.css diff --git a/app/demo/demo.module.css b/app/demo/demo.module.css new file mode 100644 index 0000000..eac2c71 --- /dev/null +++ b/app/demo/demo.module.css @@ -0,0 +1,81 @@ +.row { + display: flex; + align-items: center; + justify-content: center; +} + +.row > button { + margin-left: 4px; + margin-right: 8px; +} + +.row:not(:last-child) { + margin-bottom: 16px; +} + +.value { + font-size: 78px; + padding-left: 16px; + padding-right: 16px; + margin-top: 2px; + font-family: "Courier New", Courier, monospace; +} + +.button { + appearance: none; + background: none; + font-size: 32px; + padding-left: 12px; + padding-right: 12px; + outline: none; + border: 2px solid transparent; + color: rgb(112, 76, 182); + padding-bottom: 4px; + cursor: pointer; + background-color: rgba(112, 76, 182, 0.1); + border-radius: 2px; + transition: all 0.15s; +} + +.textbox { + font-size: 32px; + padding: 2px; + width: 64px; + text-align: center; + margin-right: 4px; +} + +.button:hover, +.button:focus { + border: 2px solid rgba(112, 76, 182, 0.4); +} + +.button:active { + background-color: rgba(112, 76, 182, 0.2); +} + +.asyncButton { + composes: button; + position: relative; +} + +.asyncButton:after { + content: ""; + background-color: rgba(112, 76, 182, 0.15); + display: block; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + opacity: 0; + transition: + width 1s linear, + opacity 0.5s ease 1s; +} + +.asyncButton:active:after { + width: 0%; + opacity: 1; + transition: 0s; +} \ No newline at end of file diff --git a/app/demo/page.tsx b/app/demo/page.tsx index b15c526..65f19e8 100644 --- a/app/demo/page.tsx +++ b/app/demo/page.tsx @@ -1,7 +1,7 @@ "use client"; /* react */ -import { Suspense, useState } from "react"; +import {Suspense, useState} from "react"; // Components import CartDrawerItem from "@/components/cart-drawer/item"; @@ -15,361 +15,448 @@ import Breadcrumbs from "@/components/ui/breadcrumbs"; import Marker from "@/components/ui/marker"; import Tag from "@/components/ui/tag"; import Badge from "@/components/ui/badge"; -import { AlertTriangle, Check, ListOrdered, Truck } from "lucide-react"; +import {AlertTriangle, Check, ListOrdered, Truck} from "lucide-react"; import Header from "@/components/layout/header"; import Footer from "@/components/layout/footer"; import Nav from "@/components/layout/nav"; -import { SortingType } from "@/lib/types"; +import {SortingType} from "@/lib/types"; import Sorting from "@/components/pages/shop/sorting"; import CartDrawer from "@/components/cart-drawer"; +/* hooks*/ +import {useAppDispatch} from "@/hooks/use-app-dispatch"; +import {useAppSelector} from "@/hooks/use-app-selector"; + +/* lib */ +import { + decrement, + increment, + incrementAsync, + incrementByAmount, + selectCount, + selectStatus, + incrementIfOdd +} from "@/lib/features/counter/counterSlice"; + +/* styles */ +import styles from "./demo.module.css" + // --- Helper Component for Layout --- const DemoSection = ({ - title, - children, -}: { - title: string; - children: React.ReactNode; + title, + children, + }: { + title: string; + children: React.ReactNode; }) => ( -
-

{title}

- {/* This inner div acts as a "canvas" for the components */} -
- {children} -
-
+
+

{title}

+ {/* This inner div acts as a "canvas" for the components */} +
+ {children} +
+
); +export default function Page() { + // State for Steppers + const [cartQuantity1, setCartQuantity1] = useState(10); + const [cartQuantity2, setCartQuantity2] = useState(10); + const [cartQuantity3, setCartQuantity3] = useState(10); + // Data for Breadcrumbs + const crumbs = [ + {label: "Scent", href: "/"}, + {label: "Shop", href: "/shop"}, + {label: "Product X", href: "/shop/product"}, + ]; -export default function Page() { - // State for Steppers - const [cartQuantity1, setCartQuantity1] = useState(10); - const [cartQuantity2, setCartQuantity2] = useState(10); - const [cartQuantity3, setCartQuantity3] = useState(10); + // State for sorting + const [sorting] = useState({criteria: "name", order: "DESC"}); - // Data for Breadcrumbs - const crumbs = [ - { label: "Scent", href: "/" }, - { label: "Shop", href: "/shop" }, - { label: "Product X", href: "/shop/product" }, - ]; + // Redux usage example: Counter + const dispatch = useAppDispatch(); + const count = useAppSelector(selectCount); + const status = useAppSelector(selectStatus); + const [incrementAmount, setIncrementAmount] = useState("2"); - // State for sorting - const [sorting] = useState({ criteria: "name", order: "DESC" }); + const incrementValue = Number(incrementAmount) || 0; - return ( -
-
-
+ ); } From 894377142d4254d5e5514989efb711bc68a8c71e Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 21:47:52 +0200 Subject: [PATCH 08/11] feat(store-provider): update Configure listeners using the provided defaults. Optional, but required for `refetchOnFocus`/`refetchOnReconnect` behaviors --- app/store-provider.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/store-provider.tsx b/app/store-provider.tsx index 9e08657..f37ce7a 100644 --- a/app/store-provider.tsx +++ b/app/store-provider.tsx @@ -1,7 +1,8 @@ 'use client' -import { useRef } from 'react' +import {useEffect, useRef} from 'react' import { Provider } from 'react-redux' import { makeStore, AppStore } from '../lib/store' +import {setupListeners} from "@reduxjs/toolkit/query"; export default function StoreProvider({ children, @@ -18,6 +19,15 @@ export default function StoreProvider({ storeRef.current = makeStore() } + useEffect(() => { + if (storeRef.current != null) { + // configure listeners using the provided defaults + // optional, but required for `refetchOnFocus`/`refetchOnReconnect` behaviors + const unsubscribe = setupListeners(storeRef.current.dispatch); + return unsubscribe; + } + }, []); + // The code is copy-pasted from the official Redux documentation https://redux-toolkit.js.org/usage/nextjs#providing-the-store. // Disable ESLint to follow the guidelines. // eslint-disable-next-line From dd8f409d26384d93eafc1d309e8dabaa3f6fd8d9 Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 22:06:36 +0200 Subject: [PATCH 09/11] style(demo): redux UI Reformat to follow the Code Conventions. --- app/demo/demo.module.css | 81 ---------------------------------------- app/demo/page.tsx | 33 ++++++++-------- app/demo/styles.css | 28 ++++++++++++++ 3 files changed, 45 insertions(+), 97 deletions(-) delete mode 100644 app/demo/demo.module.css create mode 100644 app/demo/styles.css diff --git a/app/demo/demo.module.css b/app/demo/demo.module.css deleted file mode 100644 index eac2c71..0000000 --- a/app/demo/demo.module.css +++ /dev/null @@ -1,81 +0,0 @@ -.row { - display: flex; - align-items: center; - justify-content: center; -} - -.row > button { - margin-left: 4px; - margin-right: 8px; -} - -.row:not(:last-child) { - margin-bottom: 16px; -} - -.value { - font-size: 78px; - padding-left: 16px; - padding-right: 16px; - margin-top: 2px; - font-family: "Courier New", Courier, monospace; -} - -.button { - appearance: none; - background: none; - font-size: 32px; - padding-left: 12px; - padding-right: 12px; - outline: none; - border: 2px solid transparent; - color: rgb(112, 76, 182); - padding-bottom: 4px; - cursor: pointer; - background-color: rgba(112, 76, 182, 0.1); - border-radius: 2px; - transition: all 0.15s; -} - -.textbox { - font-size: 32px; - padding: 2px; - width: 64px; - text-align: center; - margin-right: 4px; -} - -.button:hover, -.button:focus { - border: 2px solid rgba(112, 76, 182, 0.4); -} - -.button:active { - background-color: rgba(112, 76, 182, 0.2); -} - -.asyncButton { - composes: button; - position: relative; -} - -.asyncButton:after { - content: ""; - background-color: rgba(112, 76, 182, 0.15); - display: block; - position: absolute; - width: 100%; - height: 100%; - left: 0; - top: 0; - opacity: 0; - transition: - width 1s linear, - opacity 0.5s ease 1s; -} - -.asyncButton:active:after { - width: 0%; - opacity: 1; - transition: 0s; -} \ No newline at end of file diff --git a/app/demo/page.tsx b/app/demo/page.tsx index 65f19e8..324fa0b 100644 --- a/app/demo/page.tsx +++ b/app/demo/page.tsx @@ -39,7 +39,7 @@ import { } from "@/lib/features/counter/counterSlice"; /* styles */ -import styles from "./demo.module.css" +import "./styles.css"; // --- Helper Component for Layout --- const DemoSection = ({ @@ -403,51 +403,52 @@ export default function Page() {
-
+
- {count} + + {count} +
-
+
{ - setIncrementAmount(e.target.value); - }} + onChange={(e) => setIncrementAmount(e.target.value)} /> diff --git a/app/demo/styles.css b/app/demo/styles.css new file mode 100644 index 0000000..942ee9b --- /dev/null +++ b/app/demo/styles.css @@ -0,0 +1,28 @@ +@reference "../globals.css" + +.async-button { + @apply relative; +} + +.async-button::after { + content: ""; + background-color: rgba(112, 76, 182, 0.15); + display: block; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + opacity: 0; + transition: width 1s linear, opacity 0.5s ease 1s; +} + +.async-button:active::after { + width: 0; + opacity: 1; + transition: none; +} + +.row:not(:last-child) { + @apply mb-4; +} \ No newline at end of file From d5ba5ef9d0b0dd20850ba7985dc81401f5579518 Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 22:21:09 +0200 Subject: [PATCH 10/11] chore(coutnerSlice): resolve an ESLint warning Remove unused import --- lib/features/counter/counterSlice.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/features/counter/counterSlice.ts b/lib/features/counter/counterSlice.ts index 8ddf14d..4b4dda6 100644 --- a/lib/features/counter/counterSlice.ts +++ b/lib/features/counter/counterSlice.ts @@ -1,4 +1,4 @@ -import {asyncThunkCreator, PayloadAction} from "@reduxjs/toolkit"; +import {PayloadAction} from "@reduxjs/toolkit"; import {createAppSlice} from "@/lib/createAppSlice"; import {fetchCount} from "@/lib/features/counter/counterAPI"; import {AppThunk} from "@/lib/store"; From 819317f712556dddbf3b1bff97aaf21bdcda5df9 Mon Sep 17 00:00:00 2001 From: Nazar Kyselov Date: Mon, 6 Jul 2026 22:23:46 +0200 Subject: [PATCH 11/11] fix(app/api/counter/route): a TypeScript error Remove the second param `context` of the `POST` function to eliminate a TypeScript error as its not even used. --- app/api/counter/route.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/api/counter/route.ts b/app/api/counter/route.ts index 1089228..ca485b3 100644 --- a/app/api/counter/route.ts +++ b/app/api/counter/route.ts @@ -1,11 +1,7 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; -interface Context { - params: undefined; -} - -export async function POST(request: NextRequest, context: Context) { +export async function POST(request: NextRequest) { const body: { amount: number } = await request.json(); const { amount = 1 } = body;