NextJS:生产中未定义的上下文值(在开发中工作正常)
Posted
技术标签:
【中文标题】NextJS:生产中未定义的上下文值(在开发中工作正常)【英文标题】:NextJS: Context values undefined in production (works fine in development) 【发布时间】:2021-12-31 14:41:12 【问题描述】:使用React's Context api
在我的Next.js
应用程序上实现了“暗模式”功能。
在开发过程中一切正常,但是在构建版本上出现了与Context provider
相关的问题——全局状态显示为undefined
,无法处理。
_app.tsx
被 ThemeProvider
这样包裹:
// React & Next hooks
import React, useEffect from "react";
import type AppProps from "next/app";
import useRouter from "next/router";
// Irrelevant imports
// Global state management
import Provider from "react-redux";
import store from "../redux/store";
import AuthProvider from "../context/UserContext";
import ThemeProvider from "../context/ThemeContext";
// Components
import Layout from "../components/Layout/Layout";
import Footer from "../components/Footer/Footer";
// Irrelevant code
function MyApp( Component, pageProps : AppProps)
const router = useRouter();
// Applying different layouts depending on page
switch (Component.name)
case "HomePage":
return (
<Provider store=store>
<ThemeProvider>
<AuthProvider>
<Component ...pageProps />
<Footer color="fff" />
</AuthProvider>
</ThemeProvider>
</Provider>
);
case "PageNotFound":
return (
<>
<Component ...pageProps />
<Footer color="#f2f2f5" />
</>
);
default:
// Irrelevant code
export default MyApp;
ThemeContext
正确导出其Provider
和Context
:
import createContext, ReactNode, useState, useEffect from "react";
type themeContextType =
darkMode: boolean | null;
toggleDarkMode: () => void;
;
type Props =
children: ReactNode;
;
// Checks for user's preference.
const getPrefColorScheme = () =>
return !window.matchMedia
? null
: window.matchMedia("(prefers-color-scheme: dark)").matches;
;
// Gets previously stored theme if it exists.
const getInitialMode = () =>
const isReturningUser = "dark-mode" in localStorage; // Returns true if user already used the website.
const savedMode = localStorage.getItem("dark-mode") === "true" ? true : false;
const userPrefersDark = getPrefColorScheme(); // Gets user's colour preference.
// If mode was saved ► return saved mode else get users general preference.
return isReturningUser ? savedMode : userPrefersDark ? true : false;
;
export const ThemeContext = createContext<themeContextType>(
as themeContextType
);
export const ThemeProvider = ( children : Props) =>
// localStorage only exists on the browser (window), not on the server
const [darkMode, setDarkMode] = useState<boolean | null>(null);
// Getting theme from local storage upon first render
useEffect(() =>
setDarkMode(getInitialMode);
, []);
// Prefered theme stored in local storage
useEffect(() =>
localStorage.setItem("dark-mode", JSON.stringify(darkMode));
, [darkMode]);
const toggleDarkMode = () =>
setDarkMode(!darkMode);
;
return (
<ThemeContext.Provider value= darkMode, toggleDarkMode >
children
</ThemeContext.Provider>
);
;
负责更新darkMode
状态的ThemeToggler
在开发过程中正常运行(主题切换和正确值console.log
ed 在点击时),但是它在生产过程中没有做任何事情(console.log
s 和@987654337 @状态):
import React, FC, useContext from "react";
import ThemeContext from "../../context/ThemeContext";
const ThemeToggler: FC = () =>
const darkMode, toggleDarkMode = useContext(ThemeContext);
const toggleTheme = () =>
console.log(darkMode) // <--- darkMode is undefined during production
toggleDarkMode();
;
return (
<div className="theme-toggler">
<i
className=`fas $darkMode ? "fa-sun" : "fa-moon"`
data-testid="dark-mode"
onClick=toggleTheme
></i>
</div>
);
;
export default ThemeToggler;
我在发布之前查看的解决方案/建议无济于事。
React Context API undefined in production — react
和 react-dom
在同一版本上。
提前致谢。
附:对于那些想知道为什么我同时使用 Redux
和 Context
进行全局状态管理的人:
Context
最适合低频和简单的状态更新,例如 themes 和 authentication。
Redux
除了提供更好的调试工具——Redux DevTools
,更适合高频和复杂的状态更新。
P.S.2 是的,在性能方面,安装 FontAwesome 的依赖项比使用 CDN 更好。
【问题讨论】:
【参考方案1】:感谢分享代码。它写得很好。通过阅读它,我没有看到任何问题。根据您的组件拓扑,只要您的ThemeToggler
定义在任何页面组件下,您的darkMode
就不能是undefined
。
这是您的网站拓扑
<MyApp>
<Provider>
// A. will not work
<ThemeProvider>
<HomePage>
// B. should work
</HomePage>
</ThemeProvider>
// C. will not work
</Provider>
</MyApp>
虽然您的ThemeProvider
是自定义提供程序,但在ThemeContext.Provider
内部定义了值 darkMode, toggleDarkMode
。所以理论上你不能得到undefined
,除非你的组件ThemeToggler
不在HomePage
组件下。我标记了两个非工作位置,任何放在位置 A 或 C 下的组件都会给你undefined
。
由于您有HomePage
的条件,如果您在其他页面上,您可能会遇到此问题。所以一般来说你应该把ThemeProvider
包裹在你的路由器上。
<ThemeProvider>
<AuthProvider>
Component.name != "PageNotFound" && (
<Component ...pageProps />
)
</AuthProvider>
</ThemeProvider>
你明白了,在你启动路由器之前,你想先通过一个主题总是存在的层。
您可以通过以下测试来确认是否是这种情况。
function MyApp( Component, pageProps : AppProps)
return (
<ThemeProvider>
<AuthProvider>
<Component ...pageProps />
</AuthProvider>
</ThemeProvider>
)
如果这在生产中有效,那么它会确认它。说实话,这个问题在开发中也存在,不过可能是你的路由变化太快,一般都会隐藏这些问题。
【讨论】:
感谢windmaomao的详细回复!您的解决方案解决了我的问题,这确实是因为没有将其他页面与提供者一起包装。这也是一个问题,因为布局、开发和生产产生了不同的结果。 酷,我很高兴它有帮助 :) 我也在摸不着头脑,为什么你可以从一个写得很好的提供商那里得到一个undefined
。以上是关于NextJS:生产中未定义的上下文值(在开发中工作正常)的主要内容,如果未能解决你的问题,请参考以下文章
nextjs POST API 不能在实时环境中工作,但在本地环境中工作完美
如何使用来自@mui/material 的样式使情感组件选择器在 nextjs 应用程序中工作?
NuxtJS:在开发中工作但在生产中工作的路由(netlify)