xeoneux / next-dark-mode

🌑 Enable dark mode for Next.js apps
https://next-dark-mode.vercel.app
MIT License
218 stars 8 forks source link

Always forcing dark mode even if I set the mode to light #36

Open leandroluk opened 1 year ago

leandroluk commented 1 year ago

I created a simple NextJS project to test this lib and I'm using MantineUI with dark mode active on my system. The implementation is like this:

// src/pages/_app.tsx
import { MantineProvider, MantineThemeOverride } from '@mantine/core';
import { useHotkeys } from '@mantine/hooks';
import withDarkMode, { MODE, useDarkMode } from 'next-dark-mode';
import { AppProps } from 'next/app';

function App({ Component, pageProps }: AppProps) {
  const { darkModeActive, switchToDarkMode, switchToLightMode } = useDarkMode();

  useHotkeys([['mod+J', darkModeActive ? switchToLightMode : switchToDarkMode]]);

  return (
    <MantineProvider 
      withGlobalStyles 
      withNormalizeCSS 
      theme={{ colorScheme: darkModeActive ? 'dark' : 'light' }}>
      <Component {...pageProps} />
    </MantineProvider>
  );
}

export default withDarkMode(App, { defaultMode: MODE.LIGHT });
// src/pages/index.tsx

export default function Index() {
  return 'Index'
}

When starting the project and opening the index page, dark mode is activated. If I use the key combination "CMD + J" (or "CTRL + J" on Windows), the theme will change to light mode, but if the page is refreshed, this change will not be kept.

How to solve this?

leandroluk commented 1 year ago

I created a clone of the library inside the project and created a solution to the problem, Here's the solution:

1ª create a new variable in configuration

// config.ts

export interface Config {
  disableAutoMode: boolean
  // ...
}

export const defaultConfig: Config = {
  disableAutoMode: false,
  // ...
}

2º update the context to exists the new config

// darkModeContext.ts

export const DarkModeContext = createContext({
  disableAutoMode: defaultConfig.disableAutoMode,
  // ...
})

3ª update the HOC to no run darkmodejs.onChange if this config is passed:

// index.tsx

// ...

export default function withDarkMode(App: NextLayoutComponentType | any, config?: Partial<Config>) {
  const { autoModeCookieName, cookieOptions, darkModeCookieName, defaultMode, disableAutoMode } = {
    ...defaultConfig,
    ...config,
  };

  function DarkMode({ autoMode, darkMode, ...props }: AppProps) {
    const [state, setState] = useState({
      disableAutoMode,
      // ...
    })

    useEffect(() => {
      const { removeListeners } = darkmodejs({
        onChange: (activeTheme, themes) => {
          if (disableAutoMode) return;
          switch (activeTheme) {
            // ...
          }
        },
      });

      return removeListeners;
    }, []);

    const app = <App darkMode={state} {...props} />;

    return <DarkModeContext.Provider value={state}>{app}</DarkModeContext.Provider>;
  }

  DarkMode.getInitialProps = async (appContext: AppContext) => {
    / ...
  };

  DarkMode.displayName = `withDarkMode(${App.displayName || App.name || 'App'})`;

  return DarkMode;
}

export { MODE, useDarkMode };

4º add the configuration when use the HOF

import withDarkMode, { MODE, useDarkMode } from '$/libs/darkMode';
import { AppProps } from 'next/app';

function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

export default withDarkMode(App, { disableAutoMode: true });
xeoneux commented 1 year ago

Hi @leandroluk This would only happen in development and not production because of how React 18 reruns the component lifecycle. I have yet to think of a solution to this but for the time being you can switch strictMode off in next.config.js

const nextConfig = {
  reactStrictMode: false
}
leandroluk commented 1 year ago

Ho @xeoneux,

I brought you a solution, adding just an "if" and a configuration variable to the lib in a simple way, no?