【问题标题】:How to use "next-redux-wrapper" with "Next.js", "Redux-ToolKit" and Typescript properly?如何正确使用带有“Next.js”、“Redux-ToolKit”和 Typescript 的“next-redux-wrapper”?
【发布时间】:2022-01-22 08:58:49
【问题描述】:

我在 Next.js 应用程序中使用 RTK (redux-toolkit)。我正在尝试在“getInitialProps”中调度一个 AsyncThunk Action。搜索时,我发现了一个名为“next-redux-wrapper”的包,它公开了“getInitialProps”中的“store”,但我正在努力弄清楚如何让它与我的项目一起使用。

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

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

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>
>;

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

这就是我的“_app.tsx”的样子......

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;

我需要能够在此页面的“getInitialProps”中调度“getKanyeQuote”操作...

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;

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

非常感谢任何帮助。

【问题讨论】:

    标签: typescript redux next.js redux-thunk redux-toolkit


    【解决方案1】:

    WORKING DEMO

    首先,配置包装器:

    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 });
    
    

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

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

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

    包装你的 _app.tsx

    无需提供Provider 并存储,因为wrapper 会相应地进行:

    import type { AppProps } from 'next/app';
    import { wrapper } from '../app/store';
    
    function MyApp({ Component, pageProps }: AppProps) {
      return <Component {...pageProps} />;
    }
    
    export default wrapper.withRedux(MyApp);
    
    

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

    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;
    
    
    

    【讨论】:

    • 您的回答效果很好。但是 typescript 对 kanye.getInitialPropsasync () =&gt; { 不满意。而这部分const reducer = (state, action) =&gt; {
    • @Ruby 我修复了类型错误。请检查一下。谢谢!
    • 效果很好。谢谢你。
    【解决方案2】:

    按照Usage guide 上的next-redux-wrapper 回购。在您的存储文件中将是

    import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit';
    import { createWrapper, HYDRATE } from 'next-redux-wrapper';
    import { counterReducer } from '../features/counter';
    import { kanyeReducer } from '../features/kanye';
    
    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>
    >;
    
    const makeStore = () => store;
    
    export const wrapper = createWrapper(makeStore);
    

    和 _app.js 文件更改如下

    import type { AppProps } from 'next/app';
    import { wrapper } from '../app/store';
    
    function MyApp({ Component, pageProps }: AppProps) {
      return <Component {...pageProps} />;
    }
    
    export default wrapper.withRedux(MyApp);
    

    然后直接到/kanye页面应该可以了

    【讨论】:

      猜你喜欢
      • 2022-01-26
      • 2020-10-01
      • 2018-06-04
      • 2022-10-14
      • 2021-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-24
      相关资源
      最近更新 更多