对 Next.js 使用 Redux 是一种反模式吗?

我正在构建一个 Next.js 应用程序,它目前正在使用 Redux。在构建它的过程中,我想知道使用 Redux 是否真的有必要,它的使用是否实际上是一种反模式。我的理由是:

为了在 Next.js 中正确地初始化 Redux Store,您必须使用 getInitialProps方法创建一个定制的 App组件。通过这样做,您将禁用 Next.js 提供的 自动静态优化

相比之下,如果我要在客户端包含 Redux,只有在应用程序挂载之后,那么 Redux 存储将在每次服务器端导航之后重置。例如,我有一个 Next.js 应用程序,它在客户端初始化 Redux 存储,但是当路由到一个动态路由(如 pages/projects/[id])时,页面在服务器端呈现,我必须重新获取存储中的所有信息。

我的问题是:

  1. 在这种情况下使用 Redux 存储有什么好处?
  2. 我应该在根 App组件中初始化存储并放弃自动静态优化吗?
  3. 在使用 getStaticProps其他数据获取方法的 Next.js 9.3中,是否有更好的方法来管理状态
  4. 我错过了什么吗?
84615 次浏览

如果您有一个带有 getInitialProps 的自定义应用程序,则 Js 提供的静态优化将全部禁用 页数。

是的,如果你遵循这种方法。

还有更好的办法吗?

是的,您可以创建一个 Redux Provider 作为包装器并包装您需要的组件,将自动初始化 Redux 上下文并在该组件中提供。

例如:

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


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


export default withRedux(IndexPage)

您现在有可能只对需要状态管理的页面使用 Redux,而不禁用整个 App 的优化。

回答你的问题“ 对 Next.js 使用 Redux 是一种反模式吗?

没有,但需要正确使用。

更多关于如何完成这里的信息: https://github.com/vercel/next.js/tree/canary/examples/with-redux

希望这个能帮上忙

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

在组件之间传递数据。

如果你不使用还原,那么你需要做螺旋钻井。为了决定用户是否登录,我们获取数据并将其存储在 redux 存储中,然后 Header 组件连接到存储并获取身份验证信息。如果不使用 redux,则需要在每个页中获取用户,然后将其传递给 Header 组件。

Next.js 预先呈现每个页面。这意味着 Next.js 提前为每个页面生成 HTML,而不是让客户端 JavaScript 完成所有工作。预渲染可以带来更好的性能和搜索引擎优化。下一步,还原包装包允许您使用自动静态优化的 redux。如果你点击这个链接,就会看到一条提示: “ Next.js 在使用 MyApp 扩展 App 类时提供了通用的 getInitialProps,这个类会被包装器拾取,所以你不能扩展 App,因为你会被选择退出自动静态优化:”。我为我的项目设置了这个包,它很容易设置。

但是使用 redux 的缺点是,它不是缓存。您存储数据,然后定期重新获取它,以确保它是最新的。这是一项非常昂贵的工作。为了实现归约缓存,我们使用 重新选择库。这意味着在 redux 之上对您的项目有额外的依赖性,并将使您编写更多的代码。

有一个很好的包 好吧,它是由 next.js 创建的。失效时重新生效。它首先从 cache (stale)返回数据,然后发送获取请求,最后再次带来更新后的数据。我选择在每一页使用这个。

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。这里有一篇很好的文章,可以帮助你了解如何利用阿波罗作为国家管理机构。阿波罗成为州政府管理层.我建立了一个大型电子商务网站,我使用 redux,我在我的新应用程序,因为它是相对较小,我不使用下一个 js,使它更复杂。

Redux Toolkit Query

我认为 redux toolkit query (RTK query)是还原生态系统中最大的改进。它实际上是构建在 redux-toolkit 库之上的。通过后台使用 immer.js,redux-toolkit帮助我们编写更简单的 redux 代码,并更容易地更新状态。

通过“ RTK 查询”,我们可以一起处理数据获取和状态管理。所有数据获取都在一个 API 下组合,我们可以缓存数据、使缓存失效或重新获取查询。它实际上做的是 swrcontext Api的组合所做的。具有 swr 和上下文 API 的状态管理

我个人认为使用 Redux 在任何情况下都不是一个好主意。最好使用 useContext,或者在极度需要集中存储的情况下使用 mobx。但事实上,有一种简单的方法可以在不使用 getInitialProps 的情况下将 Redux 与 SSR 一起使用。

这里有一个重点-我给出的解决方案是适用的,只有当你不使用渲染的字面上的每一个页面在服务器上-当按照路线后,第一次渲染,应用程序呈现下一个页面自己。在这个解决方案中,假设存储将在服务器端初始化一次,然后呈现结果将传输到客户端。如果每次导航路由时都需要在服务器上完全呈现页面,并且需要保存存储状态,那么或许您最好还是考虑下一个 redux 包装器。

因此,要在 getServerSideProps 初始化 store,首先需要修改存储初始化文件,如下所示(可能还有其他导入) :

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

另外不要忘记,如果需要访问 your _ app.js 中的 store,则必须将 store 定义为:

const store = initializeStore(pageProps.initialReduxState);

如果使用的是 Redux,则不需要在 _ app.js 上使用 getInitialProps。

您可以使用 next-redux-wrapper,并且只需将其导出为 wraps _ app.js 即可。

存储示例,使用 next-redux-wrapper 和 thunk:

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

然后在 your _ app.js 中,将其作为函数组件导出

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

非常有效,只要确保你在做水化 ssr-> csr。

Js 只是 React 之上的一个框架,它简化了服务器端渲染设置,但它仍然是 React。React/Redux 组合非常流行,并且仍然经常被使用,我也是这样,所以答案是——这是不必要的,但是完全可能的!应用程序越大,你越喜欢函数式编程,那么 Redux 将是一个更好的选择!