繁体   English   中英

NextJS:生产中未定义的上下文值(在开发中工作正常)

[英]NextJS: Context values undefined in production (works fine in development)

使用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 state 的ThemeToggler在开发过程中正常运行(主题切换和正确的值console.log在点击时),但是它在生产过程中没有做任何事情( console.logundefined的状态):

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 上下文 API 在生产中未定义——reactreact react-dom在同一个版本上。

提前致谢。


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

  • Context最适合低频和简单的 state 更新,例如主题身份验证

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

PS2 是的,安装 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>

你明白了,在你启动路由器之前,你想首先通过一个主题总是存在的层 go。

您可以通过执行以下测试来确认是否是这种情况。

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider>
      <AuthProvider>
         <Component {...pageProps} />
      </AuthProvider>
    </ThemeProvider>
  )
}

如果这在生产中有效,则可以确认。 说实话,这个问题在开发中也存在,不过可能是你的路由变化太快,一般都会隐藏这些问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM