将Redux与Next.js一起使用是一种反模式吗?

jbose2ul  于 2022-11-12  发布在  其他
关注(0)|答案(5)|浏览(123)

我正在构建一个Next.js应用程序,它目前正在使用Redux。当我构建它的时候,我想知道Redux的使用是否真的有必要,它的使用是否实际上是一个反模式。我的理由如下:
为了在Next.js中正确初始化Redux存储,您必须使用getInitialProps方法创建一个自定义App组件。通过这样做,您将禁用Next.js提供的Automatic Static Optimization
相比之下,如果我在客户端包含Redux,那么Redux商店将在每次服务器端导航后重置。例如,我有一个Next.js应用程序,它在客户端初始化Redux商店,但当路由到动态路由(如pages/projects/[id])时,页面将在服务器端呈现,我必须重新获取商店中的任何信息。
我的问题是:
1.在这种情况下,Redux存储有什么好处?
1.我是否应该初始化根App组件中的存储,并放弃自动静态优化?
1.在Next.js 9.3中使用getStaticPropsother data fetching methods管理状态,是否有更好的方法
1.我错过了什么吗?

ffx8fchx

ffx8fchx1#

如果您有一个自定义的应用程序与getInitialProps,然后自动静态优化,Next.js提供的将被禁用的所有页面。
真的,如果你遵循这种方法。
有没有更好的办法?
是的,您可以创建一个Redux Provider作为 Package 器并 Package 您需要的组件,Redux上下文将自动初始化并在该组件中提供。
示例:

const IndexPage = () => {
  // Implementation
  const dispatch = useDispatch()
  // ...
  // ...
  return <Something />;
}

IndexPage.getInitialProps = ({ reduxStore }) => {
  // Implementation
  const { dispatch } = reduxStore;
  // ...
  // ...
}

export default withRedux(IndexPage)

现在,您可以只对需要状态管理的页面使用Redux,而无需禁用整个应用的优化。
回答您的问题“* 将Redux与Next.js一起使用是一种反模式吗?*”
不,但它需要正确使用。
有关如何完成的更多信息,请访问:https://github.com/vercel/next.js/tree/canary/examples/with-redux
我希望这对你有帮助

bxgwgixi

bxgwgixi2#

我们使用Redux主要有两个原因。

1-在组件之间传递数据。

如果您不使用redux,则需要执行适当的钻取。要确定用户是否已登录,我们将获取数据,然后将其存储在redux存储中,然后Header组件连接到存储并获取身份验证信息。如果您不使用redux,则需要获取每个页面中的用户,然后将其传递给Header组件。
Next.js会预先呈现每个页面。这意味着Next.js会提前为每个页面生成HTML,而不是让客户端JavaScript来完成。预先呈现可以带来更好的性能,并且SEO. next-redux-wrapper包允许您使用redux进行自动静态优化。如果您点击链接,会有一个提示说:“Next.js在使用类MyApp扩展应用程序时提供了通用的getInitialProps,该应用程序将由 Package 器拾取,因此您不能扩展应用程序,因为您将被排除在自动静态优化之外:“。我为我的项目设置了此包,它很容易设置。
但是使用redux的缺点是,它不是缓存。你存储数据,然后定期重新获取它以确保它是最新的。这是一个额外的昂贵的工作。为了在redux中实现缓存,我们使用reselect库。这意味着你的项目对redux的额外依赖,并将使你写更多的代码。
next.js. Stale-While-Revalidate创建了一个很好的包swr。它首先从缓存中返回数据(stale),然后发送fetch请求,最后再次返回更新的数据。我选择在每个页面中使用这个包。

import useSWR from "swr";

export const useGetUser = () => {
     // fetcher can be any asynchronous function which returns the data. useSwr will pass "/api/v1/me" to fetcher
     const { data, error, ...rest } = useSWR("/api/v1/me", fetcher);
     // !data && !error if both true, loading:true, data=null=>!data=true, error=null => !error=true
     return { data, error, loading: !data && !error, ...rest };
   };

这是可恢复的提取器

export const fetcher = (url: string) =>
  fetch(url).then(
    async (res: Response): Promise<any> => {
      const result = await res.json();

      if (res.status !== 200) {
        return Promise.reject(result);
      } else {
        return result;
      }
    }
  );

2-发出API请求。

我为我的项目设置了redux存储,它与我设置的文本编辑器冲突。Redux不知何故阻塞了编辑器,我不能用我在编辑器上写的文本填充存储。所以我使用了可重用的钩子来获取api。它一开始看起来很暗示,但如果你分析它,它会有意义。

export function useApiHandler(apiCall) {
  // fetching might have one those 3 states. you get error, you fetch the data, and you start with the loading state
  const [reqState, setReqState] = useState({
    error:null,
    data:null,
    loading:true, // initially we are loading 
  });
  const handler = async (...data) => {
    setReqState({ error: null, data: null, loading: true });
    try {
      // apiCall is a separate function to fetch the data
      const res = await apiCall(...data);
      setReqState({ error: null, data: res.data, loading: false });
      alert(res.data);// just to check it 
      return res.data;
    } catch (e) {
      // short circuting in or. if first expression is true, we dont evaluate the second.
      // short circuting in and. if first expression is true, result is the second expression
      const message =
        (e.response && e.response.data) || "Ooops, something went wrong...";
      setReqState({ error: message, data: null, loading: false });
      return Promise.reject(message);
    }
  };

  return [handler, { ...reqState }];
}

一个简单的apiCall函数

const createBlog = (data) => axios.post("/api/v1/blogs", data);

我们是这样使用它的:

export const useCreateBlog = () => useApiHandler(createBlog);

设置redux很容易,因为它很容易,人们不担心他们的应用程序的性能,他们只是设置它。在我看来,如果你有一个大的应用程序,你需要设置redux,或者如果你熟悉graphql,你可以使用阿波罗。这里有一个很好的文章,以获得一个关于使用阿波罗作为状态管理的想法。apollo as state management。我建立了一个大型电子商务网站,我使用redux,我在我的新应用程序,因为它是相对较小的,我不使用next js,使它更复杂。

Redux工具包查询

我认为redux toolkit query (RTK query)是redux生态系统中最大的改进。它实际上是建立在redux-toolkit库之上的。redux-toolkit帮助我们更简单地编写redux代码,并通过在后台使用immer.js更容易地更新状态。
使用“RTK Query”,我们可以同时处理数据提取和状态管理。所有数据提取都在一个API下组合,我们可以缓存数据、该高速缓存无效或重新提取查询。它实际上正在做swrcontext Api组合正在做的事情。使用swr和上下文API进行状态管理

zzwlnbp8

zzwlnbp83#

如果您使用的是Redux,则不需要在_app.js上具有getInitialProps。
您可以使用next-redux-wrapper,并使用它 Package _app.js导出。
使用next-redux-wrapper和形实转换的存储示例:

import { createStore, applyMiddleware } from 'redux';
import { createWrapper } from 'next-redux-wrapper';
import { composeWithDevTools } from 'redux-devtools-extension';

import thunkMiddleware from 'redux-thunk';
import rootReducer from './rootReducer';

const bindMiddleware = middleware => {
    return composeWithDevTools(applyMiddleware(...middleware));
};

const initStore = (initialState = {}) => {
    return createStore(rootReducer, initialState, bindMiddleware([thunkMiddleware]));
};

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

然后在您的_app.js中,将其导出为功能组件

const App = ({ Component, pageProps }) => {
   return (
      <Component {...pageProps} />
   )
}    
export default wrapper.withRedux(App);

工作就像一个魅力。只要确保你正在做水合ssr -〉csr。

0pizxfdo

0pizxfdo4#

Next.js只是React上的一个框架,它简化了服务器端渲染的设置,但它仍然是React。React/Redux组合非常流行,仍然经常使用,我也经常使用,所以答案是-这不是必要的,但完全可能!应用程序越大,你越喜欢函数式编程,Redux就越有可能是一个好的选择!

brjng4g3

brjng4g35#

我个人认为使用Redux在任何情况下都不是一个好主意。使用useContext会更好,或者在极端需要集中存储的情况下使用mobx。但事实上,有一个简单的方法可以在不使用getInitialProps的情况下将Redux与SSR一起使用。
这里有一点很重要--我给出的解决方案只有在您不使用服务器上的每个页面的渲染时才适用--当遵循第一次渲染之后的路线时,应用程序将自己呈现下一个页面。在此解决方案中,假定存储将在服务器端初始化一次,然后将呈现结果传输到客户端。如果每次浏览路径时都需要在服务器上呈现页面,并且需要保存存储状态,那么最好还是考虑下一个redux Package 器。
因此,要首先在getServerSideProps初始化存储,您需要按如下所示更改存储初始化文件(可能会有其他导入):

import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';

let storeInstance: any;
export const makeStore = (initialState: {}) => {
    storeInstance = createStore(
        Reducers,
        initialState,
        composeWithDevTools(applyMiddleware(thunkMiddleware)) // Optional, but is a handy thing
    );
    return storeInstance;
};

// initializeStore used for pages that need access to store at getServerSideProps
export const initializeStore = (preloadedState) => {
    let reInitiatedStore = storeInstance ?? makeStore(preloadedState)

    // After navigating to a page with an initial Redux state, merge that state
    // with the current state in the store, and create a new store
    if (preloadedState && storeInstance) {
        reInitiatedStore = makeStore({ ...storeInstance.getState(), ...preloadedState});
        // Reset the current store
        storeInstance = undefined;
    }

    // Keep in mind that in some cases this can cause strange
    // and difficult to track errors, so whether or not
    // to uncomment next lines depends on the architecture of your application.
    // if (typeof(window) === 'undefined') {
    //    return reInitiatedStore; // For SSG and SSR always create a new store
    // }

    // Create the store once in the client
    if (!storeInstance) {
        storeInstance = reInitiatedStore;
    }

    return reInitiatedStore;
}

之后,在需要存放在服务器端的页面中,在getServerSideProps中,你可以简单的使用initializeStore:

import { initializeStore } from '@Redux';

// Compnent code here...

export const getServerSideProps(context: any) {
    const reduxStore = initializeStore();
    // reduxStore = {
    // dispatch: [Function (anonymous)],
    // subscribe: [Function: subscribe],
    // getState: [Function: getState],
    // }

    // Doing something with the storage...

    const initialReduxState = storeInstance.getState(); // and get it state
    return { props: { initialReduxState, ...someProps } };
}

另外,如果您需要访问_app.js中的store,则必须将store定义为:

const store = initializeStore(pageProps.initialReduxState);

相关问题