From e2ae6e7dd600f84c8edc23fe212ecb6a6bb4d986 Mon Sep 17 00:00:00 2001 From: WebSavva Date: Tue, 4 Nov 2025 13:50:42 +0300 Subject: [PATCH 1/2] feat(store)!: Zustand was replaced with Redux --- package.json | 5 +- pnpm-lock.yaml | 238 +++++++++++++++++++--------- src/components/Cart/index.tsx | 7 +- src/components/CartButton/index.tsx | 6 +- src/components/OrderIntro/index.tsx | 4 +- src/contexts/cart/Provider.tsx | 21 +-- src/contexts/cart/context.ts | 7 - src/contexts/cart/store.ts | 158 ++++++++++-------- src/hooks/use-cart-store.ts | 65 ++++++-- 9 files changed, 333 insertions(+), 178 deletions(-) delete mode 100644 src/contexts/cart/context.ts diff --git a/package.json b/package.json index 27f90c2..c6147cf 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,10 @@ "tailwindcss-animate": "^1.0.7", "ufo": "^1.3.2", "zod": "^3.22.4", - "zustand": "^4.5.2" + "redux": "^5.0.1", + "react-redux": "^9.2.0", + "@reduxjs/toolkit": "^2.10.0", + "redux-persist": "^6.0.0" }, "devDependencies": { "@faker-js/faker": "^8.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f18ad37..6a8341b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,13 +13,13 @@ importers: version: 3.3.4(react-hook-form@7.49.3(react@18.2.0)) '@payloadcms/bundler-webpack': specifier: ^1.0.7 - version: 1.0.7(@swc/core@1.6.1(@swc/helpers@0.5.2))(ajv@8.12.0)(esbuild@0.19.11)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(sass@1.69.4) + version: 1.0.7(@swc/core@1.6.1(@swc/helpers@0.5.2))(ajv@8.12.0)(esbuild@0.19.11)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0))(sass@1.69.4) '@payloadcms/db-postgres': specifier: ^0.8.5 - version: 0.8.5(@types/react@18.2.45)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(react@18.2.0) + version: 0.8.5(@types/react@18.2.45)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0))(react@18.2.0) '@payloadcms/richtext-slate': specifier: ^1.5.2 - version: 1.5.2(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + version: 1.5.2(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.45)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -41,6 +41,9 @@ importers: '@react-email/components': specifier: ^0.0.14 version: 0.0.14(@types/react@18.2.45)(react@18.2.0) + '@reduxjs/toolkit': + specifier: ^2.10.0 + version: 2.10.0(react-redux@9.2.0(@types/react@18.2.45)(react@18.2.0)(redux@5.0.1))(react@18.2.0) '@trpc/client': specifier: ^10.45.0 version: 10.45.0(@trpc/server@10.45.0) @@ -85,7 +88,7 @@ importers: version: 6.9.8 payload: specifier: ^2.26.0 - version: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + version: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0) react: specifier: ^18 version: 18.2.0 @@ -95,9 +98,18 @@ importers: react-hook-form: specifier: ^7.49.3 version: 7.49.3(react@18.2.0) + react-redux: + specifier: ^9.2.0 + version: 9.2.0(@types/react@18.2.45)(react@18.2.0)(redux@5.0.1) react-transition-group: specifier: ^4.4.5 version: 4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + redux: + specifier: ^5.0.1 + version: 5.0.1 + redux-persist: + specifier: ^6.0.0 + version: 6.0.0(react@18.2.0)(redux@5.0.1) sonner: specifier: ^1.4.0 version: 1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -116,9 +128,6 @@ importers: zod: specifier: ^3.22.4 version: 3.22.4 - zustand: - specifier: ^4.5.2 - version: 4.5.2(@types/react@18.2.45)(immer@9.0.21)(react@18.2.0) devDependencies: '@faker-js/faker': specifier: ^8.4.1 @@ -1796,6 +1805,17 @@ packages: peerDependencies: react: 18.2.0 + '@reduxjs/toolkit@2.10.0': + resolution: {integrity: sha512-DCDKKB+DDy00kULFGoj5jtxRXksCGtEKEPbeSqRi20/vUrwrBD+zyJx+jQ7xkIU1pUHQ6OIYHyc9NiVSEjAlNA==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + '@rollup/rollup-android-arm-eabi@4.21.2': resolution: {integrity: sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==} cpu: [arm] @@ -1963,6 +1983,12 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@swc/core-darwin-arm64@1.6.1': resolution: {integrity: sha512-u6GdwOXsOEdNAdSI6nWq6G2BQw5HiSNIZVcBaH1iSvBnxZvWbnIKyDiZKaYnDwTLHLzig2GuUjjE2NaCJPy4jg==} engines: {node: '>=10'} @@ -2204,6 +2230,9 @@ packages: '@types/serve-static@1.15.5': resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@types/ws@8.5.10': resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} @@ -4014,6 +4043,9 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + immer@10.2.0: + resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + immer@9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} @@ -5553,6 +5585,18 @@ packages: react: ^16.8.0 || ^17 || ^18 react-dom: ^16.8.0 || ^17 || ^18 + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -5661,6 +5705,23 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + redux-persist@6.0.0: + resolution: {integrity: sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==} + peerDependencies: + react: '>=16' + redux: '>4.0.0' + peerDependenciesMeta: + react: + optional: true + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + reflect.getprototypeof@1.0.4: resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} engines: {node: '>= 0.4'} @@ -5686,6 +5747,9 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -6461,10 +6525,10 @@ packages: '@types/react': optional: true - use-sync-external-store@1.2.0: - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 utf8-byte-length@1.0.4: resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==} @@ -6785,21 +6849,6 @@ packages: zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - zustand@4.5.2: - resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} - engines: {node: '>=12.7.0'} - peerDependencies: - '@types/react': '>=16.8' - immer: '>=9.0.6' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} @@ -7726,33 +7775,33 @@ snapshots: '@one-ini/wasm@0.1.1': {} - '@payloadcms/bundler-webpack@1.0.7(@swc/core@1.6.1(@swc/helpers@0.5.2))(ajv@8.12.0)(esbuild@0.19.11)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(sass@1.69.4)': + '@payloadcms/bundler-webpack@1.0.7(@swc/core@1.6.1(@swc/helpers@0.5.2))(ajv@8.12.0)(esbuild@0.19.11)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0))(sass@1.69.4)': dependencies: ajv: 8.12.0 compression: 1.7.4 connect-history-api-fallback: 1.6.0 - css-loader: 5.2.7(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) - file-loader: 6.2.0(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + css-loader: 5.2.7(webpack@5.89.0) + file-loader: 6.2.0(webpack@5.89.0) find-node-modules: 2.1.3 - html-webpack-plugin: 5.5.3(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + html-webpack-plugin: 5.5.3(webpack@5.89.0) md5: 2.3.0 - mini-css-extract-plugin: 1.6.2(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + mini-css-extract-plugin: 1.6.2(webpack@5.89.0) path-browserify: 1.0.1 - payload: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + payload: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0) postcss: 8.4.31 - postcss-loader: 6.2.1(postcss@8.4.31)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + postcss-loader: 6.2.1(postcss@8.4.31)(webpack@5.89.0) postcss-preset-env: 9.0.0(postcss@8.4.31) process: 0.11.10 - sass-loader: 12.6.0(sass@1.69.4)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) - style-loader: 2.0.0(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) - swc-loader: 0.2.3(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) - swc-minify-webpack-plugin: 2.1.1(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) - terser-webpack-plugin: 5.3.10(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) - url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + sass-loader: 12.6.0(sass@1.69.4)(webpack@5.89.0) + style-loader: 2.0.0(webpack@5.89.0) + swc-loader: 0.2.3(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0) + swc-minify-webpack-plugin: 2.1.1(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0) + terser-webpack-plugin: 5.3.10(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@5.89.0) webpack: 5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0) webpack-bundle-analyzer: 4.10.1 webpack-cli: 4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0) - webpack-dev-middleware: 6.1.2(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + webpack-dev-middleware: 6.1.2(webpack@5.89.0) webpack-hot-middleware: 2.26.0 transitivePeerDependencies: - '@swc/core' @@ -7769,13 +7818,13 @@ snapshots: - utf-8-validate - webpack-dev-server - '@payloadcms/db-postgres@0.8.5(@types/react@18.2.45)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(react@18.2.0)': + '@payloadcms/db-postgres@0.8.5(@types/react@18.2.45)(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0))(react@18.2.0)': dependencies: '@libsql/client': 0.3.6 console-table-printer: 2.11.2 drizzle-kit: 0.20.14-1f2c838 drizzle-orm: 0.29.3(@libsql/client@0.3.6)(@types/react@18.2.45)(pg@8.11.3)(react@18.2.0) - payload: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + payload: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0) pg: 8.11.3 prompts: 2.4.2 to-snake-case: 1.0.0 @@ -7807,12 +7856,12 @@ snapshots: - supports-color - utf-8-validate - '@payloadcms/richtext-slate@1.5.2(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + '@payloadcms/richtext-slate@1.5.2(payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@faceless-ui/modal': 2.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) i18next: 22.5.1 is-hotkey: 0.2.0 - payload: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + payload: 2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0) react: 18.2.0 react-i18next: 11.18.6(i18next@22.5.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) slate: 0.91.4 @@ -8302,6 +8351,18 @@ snapshots: dependencies: react: 18.2.0 + '@reduxjs/toolkit@2.10.0(react-redux@9.2.0(@types/react@18.2.45)(react@18.2.0)(redux@5.0.1))(react@18.2.0)': + dependencies: + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 10.2.0 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 18.2.0 + react-redux: 9.2.0(@types/react@18.2.45)(react@18.2.0)(redux@5.0.1) + '@rollup/rollup-android-arm-eabi@4.21.2': optional: true @@ -8408,6 +8469,10 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + '@swc/core-darwin-arm64@1.6.1': optional: true @@ -8659,6 +8724,8 @@ snapshots: '@types/mime': 3.0.4 '@types/node': 20.10.5 + '@types/use-sync-external-store@0.0.6': {} + '@types/ws@8.5.10': dependencies: '@types/node': 20.10.5 @@ -8905,7 +8972,7 @@ snapshots: '@webassemblyjs/ast': 1.11.6 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0))': + '@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0))(webpack@5.89.0)': dependencies: webpack: 5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0) webpack-cli: 4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0) @@ -9479,7 +9546,7 @@ snapshots: postcss-selector-parser: 6.0.15 postcss-value-parser: 4.2.0 - css-loader@5.2.7(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + css-loader@5.2.7(webpack@5.89.0): dependencies: icss-utils: 5.1.0(postcss@8.4.41) loader-utils: 2.0.4 @@ -10047,8 +10114,8 @@ snapshots: debug: 4.3.4 enhanced-resolve: 5.15.0 eslint: 8.56.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0))(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@8.3.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0) fast-glob: 3.3.2 get-tsconfig: 4.7.2 is-core-module: 2.13.1 @@ -10059,7 +10126,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0))(eslint@8.56.0): + eslint-module-utils@2.8.0(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -10090,7 +10157,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0))(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.15.0(eslint@8.56.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -10413,7 +10480,7 @@ snapshots: dependencies: flat-cache: 3.2.0 - file-loader@6.2.0(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + file-loader@6.2.0(webpack@5.89.0): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 @@ -10788,7 +10855,7 @@ snapshots: htmlparser2: 8.0.2 selderee: 0.11.0 - html-webpack-plugin@5.5.3(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + html-webpack-plugin@5.5.3(webpack@5.89.0): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -10869,6 +10936,8 @@ snapshots: ignore@5.3.2: {} + immer@10.2.0: {} + immer@9.0.21: {} immutable@4.3.4: {} @@ -11403,7 +11472,7 @@ snapshots: min-indent@1.0.1: {} - mini-css-extract-plugin@1.6.2(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + mini-css-extract-plugin@1.6.2(webpack@5.89.0): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 @@ -11773,7 +11842,7 @@ snapshots: pause@0.0.1: {} - payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + payload@2.26.0(@swc/helpers@0.5.2)(@types/react@18.2.45)(esbuild@0.19.11)(typescript@5.3.3)(webpack@5.89.0): dependencies: '@date-io/date-fns': 2.16.0(date-fns@2.30.0) '@dnd-kit/core': 6.0.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -11809,7 +11878,7 @@ snapshots: graphql-query-complexity: 0.12.0(graphql@16.8.1) graphql-scalars: 1.22.2(graphql@16.8.1) graphql-type-json: 0.3.2(graphql@16.8.1) - html-webpack-plugin: 5.5.3(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + html-webpack-plugin: 5.5.3(webpack@5.89.0) http-status: 1.6.2 i18next: 22.5.1 i18next-browser-languagedetector: 6.1.8 @@ -11858,8 +11927,8 @@ snapshots: scheduler: 0.23.0 scmp: 2.1.0 sharp: 0.32.6 - swc-loader: 0.2.3(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) - terser-webpack-plugin: 5.3.9(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + swc-loader: 0.2.3(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0) + terser-webpack-plugin: 5.3.9(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0) ts-essentials: 7.0.3(typescript@5.3.3) use-context-selector: 1.4.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(scheduler@0.23.0) uuid: 9.0.1 @@ -12103,7 +12172,7 @@ snapshots: optionalDependencies: postcss: 8.4.32 - postcss-loader@6.2.1(postcss@8.4.31)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + postcss-loader@6.2.1(postcss@8.4.31)(webpack@5.89.0): dependencies: cosmiconfig: 7.1.0 klona: 2.0.6 @@ -12486,6 +12555,15 @@ snapshots: react-fast-compare: 3.2.2 warning: 4.0.3 + react-redux@9.2.0(@types/react@18.2.45)(react@18.2.0)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 18.2.0 + use-sync-external-store: 1.6.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.45 + redux: 5.0.1 + react-refresh@0.14.2: {} react-remove-scroll-bar@2.3.4(@types/react@18.2.45)(react@18.2.0): @@ -12621,6 +12699,18 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + redux-persist@6.0.0(react@18.2.0)(redux@5.0.1): + dependencies: + redux: 5.0.1 + optionalDependencies: + react: 18.2.0 + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + reflect.getprototypeof@1.0.4: dependencies: call-bind: 1.0.5 @@ -12652,6 +12742,8 @@ snapshots: requires-port@1.0.0: {} + reselect@5.1.1: {} + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 @@ -12761,7 +12853,7 @@ snapshots: dependencies: truncate-utf8-bytes: 1.0.2 - sass-loader@12.6.0(sass@1.69.4)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + sass-loader@12.6.0(sass@1.69.4)(webpack@5.89.0): dependencies: klona: 2.0.6 neo-async: 2.6.2 @@ -13087,7 +13179,7 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 4.1.0 - style-loader@2.0.0(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + style-loader@2.0.0(webpack@5.89.0): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 @@ -13130,12 +13222,12 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - swc-loader@0.2.3(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + swc-loader@0.2.3(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0): dependencies: '@swc/core': 1.6.1(@swc/helpers@0.5.2) webpack: 5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0) - swc-minify-webpack-plugin@2.1.1(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + swc-minify-webpack-plugin@2.1.1(@swc/core@1.6.1(@swc/helpers@0.5.2))(webpack@5.89.0): dependencies: '@swc/core': 1.6.1(@swc/helpers@0.5.2) webpack: 5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0) @@ -13208,7 +13300,7 @@ snapshots: fast-fifo: 1.3.2 streamx: 2.15.6 - terser-webpack-plugin@5.3.10(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + terser-webpack-plugin@5.3.10(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0): dependencies: '@jridgewell/trace-mapping': 0.3.20 jest-worker: 27.5.1 @@ -13220,7 +13312,7 @@ snapshots: '@swc/core': 1.6.1(@swc/helpers@0.5.2) esbuild: 0.19.11 - terser-webpack-plugin@5.3.9(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + terser-webpack-plugin@5.3.9(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0): dependencies: '@jridgewell/trace-mapping': 0.3.20 jest-worker: 27.5.1 @@ -13448,14 +13540,14 @@ snapshots: dependencies: punycode: 2.3.1 - url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + url-loader@4.1.1(file-loader@6.2.0(webpack@5.89.0))(webpack@5.89.0): dependencies: loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 webpack: 5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0) optionalDependencies: - file-loader: 6.2.0(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + file-loader: 6.2.0(webpack@5.89.0) url-parse@1.5.10: dependencies: @@ -13490,7 +13582,7 @@ snapshots: optionalDependencies: '@types/react': 18.2.45 - use-sync-external-store@1.2.0(react@18.2.0): + use-sync-external-store@1.6.0(react@18.2.0): dependencies: react: 18.2.0 @@ -13618,7 +13710,7 @@ snapshots: webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0): dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0))(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0))(webpack@5.89.0) '@webpack-cli/info': 1.5.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0)) '@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.89.0)) colorette: 2.0.20 @@ -13633,7 +13725,7 @@ snapshots: optionalDependencies: webpack-bundle-analyzer: 4.10.1 - webpack-dev-middleware@6.1.2(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)): + webpack-dev-middleware@6.1.2(webpack@5.89.0): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -13685,7 +13777,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack-cli@4.10.0)) + terser-webpack-plugin: 5.3.10(@swc/core@1.6.1(@swc/helpers@0.5.2))(esbuild@0.19.11)(webpack@5.89.0) watchpack: 2.4.0 webpack-sources: 3.2.3 optionalDependencies: @@ -13818,11 +13910,3 @@ snapshots: yoctocolors@2.1.1: {} zod@3.22.4: {} - - zustand@4.5.2(@types/react@18.2.45)(immer@9.0.21)(react@18.2.0): - dependencies: - use-sync-external-store: 1.2.0(react@18.2.0) - optionalDependencies: - '@types/react': 18.2.45 - immer: 9.0.21 - react: 18.2.0 diff --git a/src/components/Cart/index.tsx b/src/components/Cart/index.tsx index c716923..d026109 100644 --- a/src/components/Cart/index.tsx +++ b/src/components/Cart/index.tsx @@ -38,7 +38,12 @@ export const Cart = ({ items: { length: itemsCount }, _isHydrated: isCartStoreLoaded, - } = useCartStore(); + } = useCartStore(({ setItems, removeItem, items, _isHydrated }) => ({ + setItems, + removeItem, + items, + _isHydrated, + })); const { pending, makeApiCall: createOrder } = useApi( () => { diff --git a/src/components/CartButton/index.tsx b/src/components/CartButton/index.tsx index fb80fe2..749f474 100644 --- a/src/components/CartButton/index.tsx +++ b/src/components/CartButton/index.tsx @@ -21,7 +21,11 @@ export const CartButton = ({ addItem, removeItem, - } = useCartStore(); + } = useCartStore(({ addItem, removeItem, items }) => ({ + addItem, + removeItem, + items, + })); const isProductAdded = items.some(({ id }) => product.id === id); diff --git a/src/components/OrderIntro/index.tsx b/src/components/OrderIntro/index.tsx index e8e0382..8f56b78 100644 --- a/src/components/OrderIntro/index.tsx +++ b/src/components/OrderIntro/index.tsx @@ -20,7 +20,9 @@ export const OrderIntro = ({ }: OrderIntroProps) => { const router = useRouter(); - const { emptyOut: emptyOutCart } = useCartStore(); + const { emptyOut: emptyOutCart } = useCartStore(({ emptyOut }) => ({ + emptyOut, + })); const { order: { id: orderId }, diff --git a/src/contexts/cart/Provider.tsx b/src/contexts/cart/Provider.tsx index e6fdf7a..3cd1831 100644 --- a/src/contexts/cart/Provider.tsx +++ b/src/contexts/cart/Provider.tsx @@ -1,25 +1,20 @@ 'use client'; -import { type PropsWithChildren, useRef } from 'react'; -import { useEffect } from 'react'; +import { type PropsWithChildren, useRef, useEffect } from 'react'; +import { Provider } from 'react-redux'; -import { CartStoreApiContext } from './context'; -import { createCartStore, type CartStoreApi } from './store'; +import { createCartStore } from './store'; export const CartStoreApiProvider = ({ children }: PropsWithChildren) => { - const cartStoreApiRef = useRef(); + const storeRef = useRef>(); - if (!cartStoreApiRef.current) { - cartStoreApiRef.current = createCartStore(); + if (!storeRef.current) { + storeRef.current = createCartStore(); } useEffect(() => { - cartStoreApiRef.current!.persist.rehydrate(); + storeRef.current?.persistor.persist(); }, []); - return ( - - {children} - - ); + return {children}; }; diff --git a/src/contexts/cart/context.ts b/src/contexts/cart/context.ts deleted file mode 100644 index f7d028c..0000000 --- a/src/contexts/cart/context.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createContext } from 'react'; - -import type { CartStoreApi } from './store'; - -// @ts-expect-error it is quit cumbersome to create default value -// for CartStoreApi -export const CartStoreApiContext = createContext(undefined); diff --git a/src/contexts/cart/store.ts b/src/contexts/cart/store.ts index 60c31c2..4ebb588 100644 --- a/src/contexts/cart/store.ts +++ b/src/contexts/cart/store.ts @@ -1,76 +1,106 @@ -import { create } from 'zustand'; -import { persist } from 'zustand/middleware'; +import { + createSlice, + configureStore, + type PayloadAction, +} from '@reduxjs/toolkit'; +import { persistStore, persistReducer } from 'redux-persist'; +import createWebStorage from 'redux-persist/lib/storage/createWebStorage'; import type { Product } from '#server/cms/collections/types'; -export interface CartStore { +const createNoopStorage = () => { + return { + getItem() { + return Promise.resolve(null); + }, + setItem(_key: string, value: number) { + return Promise.resolve(value); + }, + removeItem() { + return Promise.resolve(); + }, + }; +}; + +const storage = + typeof window !== 'undefined' + ? createWebStorage('local') + : createNoopStorage(); + +export interface CartState { items: Product[]; - _isHydrated: boolean; - - setIsHydrated: (isHydrated: boolean) => void; - - addItem: (item: Product) => void; - removeItem: (id: Product['id']) => void; - - setItems: (id: Product[]) => void; - - emptyOut: () => void; } -export const createCartStore = () => - create( - persist( - (set) => ({ - items: [], - - _isHydrated: false, - - setIsHydrated: (isHydrated: boolean) => { - set({ - _isHydrated: isHydrated, - }); - }, - - addItem: (item) => { - set((state) => { - return { items: [...state.items, item] }; - }); - }, - - removeItem: (id) => { - set((state) => { - return { - items: state.items.filter((item) => item.id !== id), - }; - }); - }, - - setItems: (items: Product[]) => { - set({ - items, - }); - }, +const cartSlice = createSlice({ + name: 'cart', + initialState: (): CartState => ({ + items: [], + _isHydrated: false, + }), + reducers: { + setIsHydrated: (state, action: PayloadAction) => { + state._isHydrated = action.payload; + }, + addItem: (state, action: PayloadAction) => { + state.items.push(action.payload); + }, + removeItem: (state, action: PayloadAction) => { + state.items = state.items.filter((item) => item.id !== action.payload); + }, + setItems: (state, action: PayloadAction) => { + state.items = action.payload; + }, + emptyOut: (state) => { + state.items = []; + }, + }, +}); + +export const { setIsHydrated, addItem, removeItem, setItems, emptyOut } = + cartSlice.actions; + +export const createCartStore = () => { + const persistedReducer = persistReducer( + { + key: 'cart-storage', + version: 1, + storage, + whitelist: ['items'], + }, + cartSlice.reducer, + ); - emptyOut: () => { - set(() => { - return { - items: [], - }; - }); - }, + const store = configureStore({ + reducer: persistedReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: false, }), - { - version: 1, - name: 'cart-storage', + }); + + const persistor = persistStore( + store, + { + // @ts-expect-error - manualPersist is not in the official types but is supported + manualPersist: true, + }, + () => { + store.dispatch(setIsHydrated(true)); + }, + ); - skipHydration: true, + return { + store, + persistor, + }; +}; - onRehydrateStorage: () => (state) => { - state?.setIsHydrated(true); - }, - }, - ), - ); +export type CartStore = ReturnType['store']; +export type CartStoreApi = CartStore; +export type RootState = ReturnType; -export type CartStoreApi = ReturnType; +export const ROOT_STATE_PROP_NAMES: (keyof RootState)[] = [ + 'items', + '_isHydrated', +]; diff --git a/src/hooks/use-cart-store.ts b/src/hooks/use-cart-store.ts index 43a1e8f..d2a839a 100644 --- a/src/hooks/use-cart-store.ts +++ b/src/hooks/use-cart-store.ts @@ -1,25 +1,64 @@ 'use client'; -import { useContext } from 'react'; -import { useStore as extractStore } from 'zustand'; +import { useSelector, useDispatch, shallowEqual } from 'react-redux'; +import { useMemo } from 'react'; -import { CartStoreApiContext } from '@/contexts/cart/context'; -import type { CartStore } from '@/contexts/cart/store'; +import type { RootState, CartState } from '@/contexts/cart/store'; +import type { Product } from '#server/cms/collections/types'; +import { + addItem as addItemAction, + removeItem as removeItemAction, + setItems as setItemsAction, + emptyOut as emptyOutAction, + setIsHydrated as setIsHydratedAction, +} from '@/contexts/cart/store'; -function useCartStore<_ = CartStore>(): CartStore; +export interface CartStore extends CartState { + addItem: (item: Product) => void; + removeItem: (id: Product['id']) => void; + setItems: (items: Product[]) => void; + emptyOut: () => void; + setIsHydrated: (isHydrated: boolean) => void; +} + +function useCartStore(): CartStore; function useCartStore(selector: (store: CartStore) => T): T; -function useCartStore(selector?: (store: CartStore) => T) { - const cartStoreApi = useContext(CartStoreApiContext); +function useCartStore(selector?: (store: CartStore) => T): T | CartStore { + const dispatch = useDispatch(); - if (!cartStoreApi) { - throw new Error(`useCartStore must be used within CartStoreApiProvider`); - } + const state = useSelector( + ({ items, _isHydrated }: RootState) => ({ + items, + _isHydrated, + }), + shallowEqual, + ); + + // Memoize actions to prevent recreating them on every render + const actions = useMemo(() => { + return { + addItem: (item: Product) => dispatch(addItemAction(item)), + removeItem: (id: Product['id']) => dispatch(removeItemAction(id)), + setItems: (items: Product[]) => dispatch(setItemsAction(items)), + emptyOut: () => dispatch(emptyOutAction()), + setIsHydrated: (isHydrated: boolean) => + dispatch(setIsHydratedAction(isHydrated)), + }; + }, [dispatch]); + + // Memoize the combined store object + const store = useMemo(() => { + return { + ...state, + ...actions, + }; + }, [state, actions]); if (selector) { - return extractStore(cartStoreApi, selector); - } else { - return extractStore(cartStoreApi); + return selector(store); } + + return store; } export { useCartStore }; From 82b16cb0664a216a5f9fe41cfd0b516c09d5bfd9 Mon Sep 17 00:00:00 2001 From: WebSavva Date: Tue, 4 Nov 2025 13:52:34 +0300 Subject: [PATCH 2/2] fix: README.md was updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ec60167..c83b197 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ - Tailwind CSS - Zod - React Transition -- Zustand +- Redux - React Hook Form - Custom Middleware Module