NextJS:生产中未定义的上下文值(在开发中工作正常)
NextJS: Context values undefined in production (works fine in development)
我的 Next.js
应用程序使用 React's Context api
实现了“深色模式”功能。
开发期间一切正常,但是,构建版本出现了 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
单击时编辑),但它在生产期间不执行任何操作(console.log
处于 undefined
状态):
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;
我在发帖前查过的 solutions/suggestions 无济于事。
React Context API undefined in production — react
和 react-dom
是同一版本。
提前致谢。
P.S。对于那些想知道为什么我同时使用 Redux
和 Context
进行全局状态管理的人:
Context
最适合低频和简单的状态更新,例如 themes 和 authentication.
Redux
除了提供更好的调试工具外,更适合高频和复杂的状态更新 — Redux DevTools
.
P.S.2 是的,就性能而言,安装 FontAwesome 的依赖项比使用 CDN 更好。
感谢您分享代码。写得很好。通过阅读它,我没有看到任何问题。根据您的组件拓扑,只要您的 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>
)
}
如果这在生产中有效,那么它证实了这一点。老实说,这个问题在开发中也存在,不过可能是你的路由变化太快,一般会把这些问题隐藏起来。
我的 Next.js
应用程序使用 React's Context api
实现了“深色模式”功能。
开发期间一切正常,但是,构建版本出现了 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
单击时编辑),但它在生产期间不执行任何操作(console.log
处于 undefined
状态):
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;
我在发帖前查过的 solutions/suggestions 无济于事。
React Context API undefined in production — react
和 react-dom
是同一版本。
提前致谢。
P.S。对于那些想知道为什么我同时使用 Redux
和 Context
进行全局状态管理的人:
Context
最适合低频和简单的状态更新,例如 themes 和 authentication.Redux
除了提供更好的调试工具外,更适合高频和复杂的状态更新 —Redux DevTools
.
P.S.2 是的,就性能而言,安装 FontAwesome 的依赖项比使用 CDN 更好。
感谢您分享代码。写得很好。通过阅读它,我没有看到任何问题。根据您的组件拓扑,只要您的 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>
)
}
如果这在生产中有效,那么它证实了这一点。老实说,这个问题在开发中也存在,不过可能是你的路由变化太快,一般会把这些问题隐藏起来。