如何使用“next-redux-wrapper"使用“Next.js"、“Redux-ToolKit"和打字稿正确吗? [英] How to use "next-redux-wrapper" with "Next.js", "Redux-ToolKit" and Typescript properly?

查看:63
本文介绍了如何使用“next-redux-wrapper"使用“Next.js"、“Redux-ToolKit"和打字稿正确吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Next.js 应用程序中使用 RTK (redux-toolkit).我正在尝试在getInitialProps"中发送一个 AsyncThunk Action.搜索时,我发现了一个名为next-redux-wrapper"的包.暴露商店"在getInitialProps"中,但我正在努力弄清楚如何使其适用于我的项目.

这是我目前使用带有 2 个减速器的 Typescript 的项目的准系统示例.一个 reducer 使用 AsyncThunk 从 API 获取数据.我已经安装了next-redux-wrapper";但我不知道如何实现它,以便所有页面都可以访问商店".在getInitialProps"中.该软件包的文档有一个示例,但令人困惑.

这是我的 store.ts 的样子...

import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit';导入 { createWrapper, HYDRATE } from 'next-redux-wrapper';从'../features/counter'导入{counterReducer};从'../features/kanye'导入{ kanyeReducer };导出常量存储 = configureStore({减速机:{计数器:counterReducer,kanyeQuote: kanyeReducer,},});导出类型 AppDispatch = typeof store.dispatch;导出类型 RootState = ReturnType;导出类型 AppThunk<ReturnType = void>= ThunkAction<返回类型,根状态,未知,动作<字符串>>;

如你所见,我导入了 next-redux-wrapper,仅此而已.

这就是我的_app.tsx"的方式.看起来……

从 'react-redux' 导入 { Provider };从下一个/应用程序"导入类型 { AppProps };从'../app/store'导入{商店};功能 MyApp({ 组件, pageProps }: AppProps) {返回 (<提供者商店={商店}><组件 {...pageProps}/></提供者>);}导出默认 MyApp;

我需要能够发送getKanyeQuote";getInitialProps"中的操作在这个页面上...

从'react'导入反应;导入 { useAppDispatch, useAppSelector } from '../app/hooks';从'../features/kanye'导入{getKanyeQuote};const kanye: React.FC = () =>{常量调度 = useAppDispatch();const { 数据,待处理,错误 } = useAppSelector((state) => state.kanyeQuote);返回 (

<h2>生成随机 Kanye West 报价</h2>{待定&&<p>加载中...</p>}{数据&&<p>{data.quote}</p>}{错误&&<p>糟糕,出了点问题</p>}<按钮点击={() =>dispatch(getKanyeQuote())} disabled={pending}>生成 Kanye 报价</按钮></div>);};导出默认kanye;

这里有一个完整示例的链接.https://stackblitz.com/edit/github-bizsur-zkcmca?file=src%2Ffeatures%2Fcounter%2Freduce.ts

非常感谢任何帮助.

解决方案

工作演示

首先,配置包装器:

导入{行动,任何动作,组合减速器,配置存储,ThunkAction,} 来自'@reduxjs/toolkit';导入 { createWrapper, HYDRATE } from 'next-redux-wrapper';从'../features/counter'导入{counterReducer};从'../features/kanye'导入{ kanyeReducer };const combineReducer = combineReducers({计数器:counterReducer,kanyeQuote: kanyeReducer,});const reducer = (state: ReturnType, action: AnyAction) =>{如果(action.type === HYDRATE){常量下一个状态 = {...state,//使用之前的状态...action.payload,//应用 delta 从 hydration};返回下一个状态;} 别的 {返回组合减速器(状态,动作);}};导出 const makeStore = () =>配置存储({减速器,});type Store = ReturnType;导出类型 AppDispatch = Store['dispatch'];导出类型 RootState = ReturnType;导出类型 AppThunk<ReturnType = void>= ThunkAction<返回类型,根状态,未知,动作<字符串>>;export const wrapper = createWrapper(makeStore, { debug: true });

这里新的reducer函数合并了新创建的服务器存储和客户端存储:

  • wrapper 使用 makeStore 函数创建一个新的服务器端 redux 存储
  • wrapper 调度 HYDRATE 动作.它的有效载荷是新创建的服务器存储
  • reducer 将服务器存储与客户端存储合并.

我们只是用服务器状态替换客户端状态,但如果存储变得复杂,可能需要进一步协调.

包装你的 _app.tsx

不需要提供 Provider 和 store 因为 wrapper 会做相应的:

import type { AppProps } from 'next/app';从'../app/store'导入{包装器};功能 MyApp({ 组件, pageProps }: AppProps) {返回 <组件 {...pageProps}/>;}导出默认 wrapper.withRedux(MyApp);

然后您可以在您的页面中发送 thunk 操作:

import { NextPage } from 'next/types';从反应"导入反应;导入 { useAppDispatch, useAppSelector } from '../app/hooks';从'../features/kanye'导入{getKanyeQuote};从'../app/store'导入{包装器};const kanye: NextPage = () =>{常量调度 = useAppDispatch();const { 数据,待处理,错误 } = useAppSelector((state) => state.kanyeQuote);返回 (

<h2>生成随机 Kanye West 报价</h2>{待定&&<p>加载中...</p>}{数据&&<p>{data.quote}</p>}{错误&&<p>糟糕,出了点问题</p>}<按钮点击={() =>dispatch(getKanyeQuote())} disabled={pending}>生成 Kanye 报价</按钮></div>);};kanye.getInitialProps = wrapper.getInitialPageProps(({ 调度 }) =>异步()=>{等待调度(getKanyeQuote());});导出默认kanye;

I'm using RTK (redux-toolkit) inside a Next.js App. And I'm trying to dispatch an AsyncThunk Action inside "getInitialProps". When searching I found a package called "next-redux-wrapper" that exposes the "store" inside "getInitialProps", but I'm struggling to figure out how to make it work with my project.

Here's a barebone sample of the project where I'm using Typescript with 2 reducers at the moment. One reducer is using AsyncThunk to get data from an API. I already installed "next-redux-wrapper" but I don't know how to implement it around the so that all pages get access to the "store" inside "getInitialProps". The Docs of that package has an example but rather a confusing one.

Here's how my store.ts looks like ...

import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit';
import { createWrapper, HYDRATE } from 'next-redux-wrapper';
import { counterReducer } from '../features/counter';
import { kanyeReducer } from '../features/kanye';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
    kanyeQuote: kanyeReducer,
  },
});

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

As you can see I imported next-redux-wrapper, but that's abuout it.

And here's how my "_app.tsx" looks like ...

import { Provider } from 'react-redux';
import type { AppProps } from 'next/app';
import { store } from '../app/store';

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}

export default MyApp;

I need to be able to dispatch the "getKanyeQuote" action in "getInitialProps" on this page ...

import React from 'react';
import { useAppDispatch, useAppSelector } from '../app/hooks';
import { getKanyeQuote } from '../features/kanye';

const kanye: React.FC = () => {
  const dispatch = useAppDispatch();
  const { data, pending, error } = useAppSelector((state) => state.kanyeQuote);

  return (
    <div>
      <h2>Generate random Kanye West quote</h2>
      {pending && <p>Loading...</p>}
      {data && <p>{data.quote}</p>}
      {error && <p>Oops, something went wrong</p>}
      <button onClick={() => dispatch(getKanyeQuote())} disabled={pending}>
        Generate Kanye Quote
      </button>
    </div>
  );
};

export default kanye;

And here's a link to a full sample. https://stackblitz.com/edit/github-bizsur-zkcmca?file=src%2Ffeatures%2Fcounter%2Freducer.ts

Any help is highly appreciated.

解决方案

WORKING DEMO

First, configure wrapper:

import {
  Action,
  AnyAction,
  combineReducers,
  configureStore,
  ThunkAction,
} from '@reduxjs/toolkit';
import { createWrapper, HYDRATE } from 'next-redux-wrapper';
import { counterReducer } from '../features/counter';
import { kanyeReducer } from '../features/kanye';

const combinedReducer = combineReducers({
  counter: counterReducer,
  kanyeQuote: kanyeReducer,
});

const reducer = (state: ReturnType<typeof combinedReducer>, action: AnyAction) => {
  if (action.type === HYDRATE) {
    const nextState = {
      ...state, // use previous state
      ...action.payload, // apply delta from hydration
    };
    return nextState;
  } else {
    return combinedReducer(state, action);
  }
};

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

type Store = ReturnType<typeof makeStore>;

export type AppDispatch = Store['dispatch'];
export type RootState = ReturnType<Store['getState']>;
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

export const wrapper = createWrapper(makeStore, { debug: true });

Here the new reducer function merges newly created server store and client store:

  • wrapper creates a new server side redux store with makeStore function
  • wrapper dispatches HYDRATE action. Its payload is newly created server store
  • reducer merges server store with client store.

We're just replacing client state with server state but further reconcilation might be required if the store grows complicated.

wrap your _app.tsx

No need to provide Provider and store because wrapper will do it accordingly:

import type { AppProps } from 'next/app';
import { wrapper } from '../app/store';

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

export default wrapper.withRedux(MyApp);

And then you can dispatch thunk action in your page:

import { NextPage } from 'next/types';
import React from 'react';
import { useAppDispatch, useAppSelector } from '../app/hooks';
import { getKanyeQuote } from '../features/kanye';
import { wrapper } from '../app/store';

const kanye: NextPage = () => {
  const dispatch = useAppDispatch();
  const { data, pending, error } = useAppSelector((state) => state.kanyeQuote);

  return (
    <div>
      <h2>Generate random Kanye West quote</h2>
      {pending && <p>Loading...</p>}
      {data && <p>{data.quote}</p>}
      {error && <p>Oops, something went wrong</p>}
      <button onClick={() => dispatch(getKanyeQuote())} disabled={pending}>
        Generate Kanye Quote
      </button>
    </div>
  );
};

kanye.getInitialProps = wrapper.getInitialPageProps(
  ({ dispatch }) =>
    async () => {
      await dispatch(getKanyeQuote());
    }
);

export default kanye;


这篇关于如何使用“next-redux-wrapper"使用“Next.js"、“Redux-ToolKit"和打字稿正确吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆