기본 테마의 플래시 방지: React 앱의 다크 모드 구현
This article is written for SSR/SSG React apps such as Next.js, GatsbyJS, but the same logic can be applied to single-page apps like CRA or other frameworks like Vuejs, Angular, etc.
왜 플래시인가?
라이트/다크 모드를 구현할 때 종종
localStorage
또는 prefers-color-scheme
와 같은 클라이언트 측 전용 기능에 도달하여 useEffect
후크에 넣어야 합니다. 이는 미리 렌더링된 HTML 및 스타일에 기본 테마가 필요함을 의미합니다. 방문자가 페이지를 열면 후크에서 예약된 업데이트가 실행되기 전에 HTML 요소가 기본 스타일로 구문 분석되고 렌더링되어 플래시가 실행됩니다.Here is an example from usehooks.com/useDarkMode.
더 나은 구현
이러한 플래시를 방지하기 위해 React 세계에서 테마를 관리하는 논리를 추출하고 HTML 요소가 구문 분석되고 렌더링되기 전에 실행되도록 HTML 요소 위에 배치된 별도의 스크립트로 이동할 수 있습니다.
<!DOCTYPE html>
<html>
<head>
<title>Create Next App</title>
<!-- links to stylesheets -->
</head>
<body>
<script>
// 🌟 logic for managing themes goes here
</script>
<div id="__next">
<!-- content -->
</div>
<script src="/bundled.js"></script>
</body>
</html>
To add the script, you will have to edit
_document.js
in Next.js projects orhtml.js
in GatsbyJS projects.
스크립트는 다음 작업을 수행합니다.
__onThemeChange
변수를 React 구성 요소에 의해 덮어쓰게 될 no-op 함수로 초기화합니다. __setPreferredTheme
을 업데이트하고 선택한 테마를 className
에 저장하는 전역localStorage;
함수를 선언합니다.localStorage
에 저장된 테마로 초기화하고 시스템 테마로 대체합니다.// wrapped as IIFE to use private variables and functions
(function () {
function setTheme(newTheme) {
document.body.className = newTheme; // "dark" or "light"
window.__theme = newTheme;
window.__onThemeChange(newTheme);
}
// this will be overwritten in our React component
window.__onThemeChange = function () {};
// this will be triggered by our React component
window.__setPreferredTheme = function (newTheme) {
setTheme(newTheme);
try {
localStorage.setItem("theme", JSON.stringify(window.__theme));
} catch (err) {}
};
// detect system theme and monitor for changes
const darkQuery = window.matchMedia("(prefers-color-scheme: dark)");
darkQuery.addListener(function (event) {
window.__setPreferredTheme(event.matches ? "dark" : "light");
});
let preferredTheme;
// try to get saved theme
try {
preferredTheme = JSON.parse(localStorage.getItem("theme"));
} catch (err) {}
// initialize preferredTheme
setTheme(preferredTheme || (darkQuery.matches ? "dark" : "light"));
})();
Please note the script is render-blocking so you should keep it short. Also, it will not be compiled by Babel so you should avoid using new JS features.
전역 스타일시트에서 CSS className을 기반으로 CSS 변수를 업데이트할 수 있습니다.
body {
--background: #faf4f8;
--text-color: rgba(0, 0, 0, 0.87);
--link: #3182ce;
}
body.dark {
--background: #1a202c;
--text-color: #f7fafc;
--link: #90cdf4;
}
이제 자신만의
ThemeProvider
및 useTheme
후크를 만들어 생성된 전역 함수를 연결할 수 있습니다.import React, { useState, useEffect, useContext } from "react";
const ThemeContext = React.createContext("light");
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState(global.window?.__theme || "light");
const toggleTheme = () => {
global.window.__setPreferredTheme(theme === "light" ? "dark" : "light");
};
useEffect(() => {
global.window.__onThemeChange = setTheme;
}, []);
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export const useTheme = () => useContext(ThemeContext);
다음은 Next.js를 사용한 데모입니다.
GatsbyJS에서 구현하려면 source code의 Dan Abramov's blog — Overreacted.io을 확인하십시오. 나는 그것으로부터이 접근 방식을 배웠습니다.
나중에 생각
React 앱을 개발할 때 우리는 React로 모든 작업을 수행하고 모든 로직을 React 구성 요소에 넣는 경향이 있습니다. 테마를 구현한 경험은 React 세계 외부에서 코드를 작성하고 나중에 React에 바인딩하는 것이 괜찮다는 것을 상기시켜 줍니다. 결국 React는 사용자 인터페이스를 구축하기 위한 라이브러리일 뿐입니다. 이 경우 원활한 사용자 경험을 생성하려면 브라우저 렌더링이 작동하는 방식을 이해하는 것이 필수적입니다.
읽어 주셔서 감사합니다. 차오!
React Summit is coming back on October 15-16. There'll be speakers like Kent C. Dodds, Max Stoiber, Sara Vieira, Sharif Shameem, and more.
Register for free before September 20: https://ti.to/gitnation/react-summit?source=REFURCR-1.
Reference
이 문제에 관하여(기본 테마의 플래시 방지: React 앱의 다크 모드 구현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/hangindev/avoid-flash-of-default-theme-an-implementation-of-dark-mode-in-react-app-4761텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)