【问题标题】:NextJS: Context values undefined in production (works fine in development)NextJS:生产中未定义的上下文值(在开发中工作正常)
【发布时间】:2021-12-31 14:41:12
【问题描述】:

使用React's Context api 在我的Next.js 应用程序上实现了“暗模式”功能。

在开发过程中一切正常,但是在构建版本上出现了与Context provider相关的问题——全局状态显示为undefined,无法处理。


_app.tsxThemeProvider 这样包裹:

// 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 正确导出其ProviderContext

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.loged 在点击时),但是它在生产过程中没有做任何事情(console.logs 和@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 productionreactreact-dom 在同一版本上。

提前致谢。


附:对于那些想知道为什么我同时使用 ReduxContext 进行全局状态管理的人:

  • Context 最适合低频和简单的状态更新,例如 themesauthentication

  • Redux 除了提供更好的调试工具——Redux DevTools,更适合高频和复杂的状态更新。

P.S.2 是的,在性能方面,安装 FontAwesome 的依赖项比使用 CDN 更好。

【问题讨论】:

    标签: reactjs typescript next.js react-context


    【解决方案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
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    • 1970-01-01
    • 2019-06-07
    • 1970-01-01
    相关资源
    最近更新 更多