我的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>
);
};
該ThemeToggler負責更新darkMode開發程序中狀態正常作業(主題切換和正確的價值console.log一旦點擊ED),但它不會做在生產程序中任何事情(console.logS上的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;
我在發布之前查找的解決方案/建議無濟于事。
React Context API 在生產中未定義——react并且react-dom在同一版本上。
提前致謝。
PS 對于那些想知道為什么我同時使用Redux和Context用于全域狀態管理的人:
Context最適合低頻和簡單的狀態更新,例如主題和身份驗證。Redux除了提供更好的除錯工具——Redux DevTools.
PS2 是的,在性能方面,安裝 FontAwesome 的依賴項比使用 CDN 更好。
uj5u.com熱心網友回復:
感謝分享代碼。寫得很好。通過閱讀它,我沒有看到任何問題。根據你的組件拓撲,只要你ThemeToggler是在任何頁面組件下定義的,你darkMode就不能是undefined.
這是您的站點拓撲
<MyApp>
<Provider>
// A. will not work
<ThemeProvider>
<HomePage>
// B. should work
</HomePage>
</ThemeProvider>
// C. will not work
</Provider>
</MyApp>
雖然您ThemeProvider是一個自定義提供者,但 insideThemeContext.Provider是用 value 定義的{{ 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>
)
}
如果這在生產中有效,那么它就證實了這一點。老實說,這個問題在dev中也存在,不過可能是你的路由變化太快,一般都隱藏了這些問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/362500.html
