next-redux-wrapper

Я пишу приложение на next.js(pages dir) в связке с next-redux-wrapper. Проблема с диспатчем данных в стейт из клиентской части(например при нажатии на кнопку), данные записываются на клиенте и при переходе на другие страницы не записываются. Конфигурация стора:


const rootReducer = combineReducers({
  productReducer,
  cartReducer,
  userReducer,
});

const makeStore = () =>
  configureStore({
    reducer: rootReducer,
  });

export const store = makeStore();

export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof makeStore>;
export type AppState = ReturnType<AppStore["getState"]>;
export type AppDispatch = AppStore["dispatch"];
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  AppState,
  unknown,
  Action
>;

export const wrapper = createWrapper<AppStore>(makeStore);

Слайсер с которым возникают проблемы(все слайсера примерно одинаковые):

interface CartState {
  products: IProductCart[];
  isLoading: boolean;
  error: string;
}

const initialState: CartState = {
  products: [],
  isLoading: false,
  error: "",
};

export const cartSlice = createSlice({
  name: "cart",
  initialState,
  reducers: {
    firstAddProductToCart(state, action: PayloadAction<IProductCart[]>) {
      state.products = action.payload;
    },
    addProductToCart(state, action: PayloadAction<IProductCart>) {
      state.products = state.products.concat(action.payload);
    },
  },

  extraReducers: {
    [HYDRATE]: (state, action) => {
      return {
        ...state,
        ...action.payload.cartReducer,
      };
    },
  },
});

export const { 
// все слайсеры
 } = cartSlice.actions;
export default cartSlice.reducer;

_app.tsx:

const App = ({ Component, pageProps }: AppProps) => {
  
  return (
    <>
      <Toaster toastOptions={{
          style: {
            background: 'rgb(51 65 85)',
            color: '#FFFFFF',
          }
        }}
      />
      <Component {...pageProps} />
    </>
  )
}

export default wrapper.withRedux(App);

Компонент в котором происходит запись данных в стейт:

interface ProductDetailsProps {
  product: IProduct
}

export const ProductDetails: React.FC<ProductDetailsProps> = ({ product }) => {    
  const router = useRouter()
  const dispatch = useDispatch()

    return (
      <div className='grid grid-cols-1 md:grid-cols-2 gap-12'>
        {/*<ProductImage cartProduct={cartProduct} product={product} handleColorSelect={handleColorSelect} />*/}
        <div className='flex flex-col gap-1 text-slate-500 text-sm'>
          <h2 className='text-3xl font-medium text-slate-700'>{product.name}</h2>
          <Horizontal />
          <div>
            <Rating value={rating} readOnly />
            <div className='flex items-center gap-2 '>{product.product_reviews.length} Отзывы</div>
          </div>
          <Horizontal />
          <div className='text-justify'>
            {product.description}
          </div>
          <Horizontal />
          <div>
            <span className='font-semibold'>Категория: </span>
            {product.category.name}
          </div>
          <div>
            <span className='font-semibold'>Brand: </span>
            {product.brand}
          </div>
          <div className={product?.on_stock ? 'text-teal-700' : 'text-rose-900'}>
            {product?.on_stock ? 'Есть в наличии' : 'Нет в наличии'}
          </div>
          <Horizontal />
          {productInCart ?
            <>
              <p className='mb-2 text-slate-500 flex items-center gap-1'>
                <MdCheckCircle size={20} className='text-teal-400' />
                <span>Добавлен в корзину</span>
              </p>
              <div className='max-w-[300px]'>
                <CartButton label='Перейти в корзину' outline onClick={() => {
                  router.push('/cart')
                }} />
              </div>
            </> :
            <>
              {/* <SetColor*/}
              {/*  cartProduct={cartProduct}*/}
              {/*  images={product.images}*/}
              {/*  handleColorSelect={handleColorSelect}*/}
              {/*/>*/}
              {/*<Horizontal /> */}
              <div className='flex gap-8 items-center'>
                <div className='font-semibold'>
                  Кол-во:
                </div>
                <div className='flex gap-4 items-center text-base'>
                  <button 
                    onClick={() => setCartCounter(prev => {
                      if (prev > 1) {
                        return --prev
                      }
                      toast.error('Это минимум ...')
                      return prev
                    })} 
                    className='border-[1.2px] border-slate-300 p-2 rounded'
                  >
                    -
                  </button>
                  <div>{cartCounter}</div>
                  <button 
                    onClick={() => setCartCounter(prev => {
                      if (prev < product.quantity) {
                        return ++prev
                      }
                      toast.error('Это максимум ...')
                      return prev
                    })} 
                    className='border-[1.2px] border-slate-300 p-2 rounded'
                  >
                    +
                  </button>
                </div>
              </div>
              <Horizontal />
              <div className='max-w-[300px]'>
                <CartButton
                  label={"Добавить в корзину"}
                  onClick={() => {
                    const productCart: IProductCart = {
                      ...product,
                      quantity: cartCounter,
                      maxQuantity: product.quantity
                    }
                    dispatch(addProduct(productCart))
                    toast.success(product.name + ' добавлен в корзину')
                  }}
                />
              </div>
            </>}
        </div>
      </div>
    )
}

Страница, внутри которой используется компонент:

interface ProductParams {
  product: IProduct
}

export default function Product({product}: ProductParams) { 

  if ('detail' in product)
    return <>Product is not found</>

  return (
    <RootLayout>
      <div className="p-8">
        <Container>
          <ProductDetails product={product} />
        </Container>
        <div className='flex flex-col mt-20 gap-4'>
          <ListRating product={product} />
        </div>
      </div>
    </RootLayout>
  )
}

export const getStaticPaths: GetStaticPaths = async () => {
  const res = await fetch('http://localhost:5000/products');
  const products: IProduct[] = await res.json();

  const paths = products.map((product) => {
    return {
      params: { id: product.id.toString() },
    };
  });

  return {
    paths,
    fallback: true,
  };
};

export const getStaticProps: GetStaticProps = async ({ params }) => {  
  try {
    const response = await fetch(`http://localhost:5000/products/${params?.id}`, {next: {revalidate: 300}});
    const product = await response.json();
    return {
      props: { product },
    };
  } catch (e) {
    console.log('Error: ', e);
    return {
      notFound: true      
    };
  }
};

Ответы (0 шт):