«Островная» архитектура — это термин, введенный относительно недавно, который описывает фронтенды, состоящие из нескольких точек входа. Она бросает вызов традиционному подходу к визуализации гигантского дерева компонентов, позволяя более четко изолировать динамические, гидратируемые элементы от статического контента. И это заложено в Fresh, новый фреймворк для Deno, который я сейчас использую для проекта (скоро будет)!
Но с этой изоляцией приходят ограничения, которые препятствуют использованию регулярных паттернов, таких как общее состояние. В этом обзоре я расскажу, как мне удалось синхронизировать состояние хуков на разных островах и сохранить логику моего приложения упорядоченной.
Хук, о котором пойдет речь ☀️/🌙
Чтобы включить предпочтение темного режима в моем проекте, я добавил этот простой хук для взаимодействия с "prefers-color-scheme: dark"
, добавив класс dark
в элемент body
и установив его в localstorage
, чтобы сохранить любое переопределение предпочтения:
export function useDarkMode() {
const [dark, setDark] = useState(false);
function toggleDarkMode() {
const prefersDark = document.body.classList.toggle('dark');
setDark(prefersDark);
localStorage.setItem('prefers-dark', prefersDark);
}
useEffect(() => {
const prefersDark = localStorage.getItem('prefers-dark') === 'true';
const devicePrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if ((prefersDark === null || prefersDark) && devicePrefersDark) {
toggleDarkMode();
}
}, []);
return [dark, toggleDarkMode];
}
Это работает для дерева рендеринга, в котором есть кнопка, запускающая toggleDarkMode
, но из-за островного подхода это дерево рендеринга (и состояние внутри него) полностью изолировано от других. Чтобы гарантировать, что все элементы находятся в правильном состоянии dark
независимо от точки входа, необходимо иметь спасательный круг между островами.
Введите: диспетчеризация событий 🛟.
Хотя существует множество подходов к решению этой проблемы (MutationObserver
и т.д.), самым простым является отправка события, которое могут прослушать другие экземпляры этого хука.
В данном случае это заставляет каждый остров вызывать функцию toggleDarkMode
и (при соответствующих условиях) синхронизировать свое состояние с состоянием сработавшего экземпляра хука. Вот изменения, необходимые для вышеупомянутого хука, чтобы добиться этого:
export function useDarkMode() {
function toggleDarkMode() {
// same code as above
window.dispatchEvent(new Event('dark-mode-preference-updated'));
}
function respondToEvent() {
const prefersDark = document.body.classList.contains('dark');
setDark(prefersDark);
}
useEffect(() => {
const prefersDark = localStorage.getItem('prefers-dark') === 'true';
const devicePrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if ((prefersDark === null || prefersDark) && devicePrefersDark) {
if (!document.body.classList.contains('dark')) {
toggleDarkMode();
} else if (!dark) {
setDark(true);
}
}
window.addEventListener('dark-mode-preference-updated', respondToEvent);
return () => {
window.removeEventListener('dark-mode-preference-updated');
};
}, []);
}
Подведем итог: каждый экземпляр хука при установке будет проверять предпочтения пользователя относительно цветовой схемы и устанавливать это значение в состоянии, вызывая одну и ту же функцию.
Затем, любой вызов toggleDarkMode
вызовет событие для каждого другого экземпляра хука, которое заставит каждый из них проверить значение на body
и сохранить его в состоянии без выполнения каких-либо мутаций.
Значение localstorage
устанавливается только срабатывающим хуком, поэтому последующие загрузки страниц будут получать правильное значение предпочтений.
Хотя это может противоречить некоторым принципам, которые хуки призваны упростить в отношении общего состояния, это позволяет логике, разделяемой между компонентами, жить в одном месте. Реализация этого хука упрощена тем, что document.body.classList
является источником истины, но для синхронизации данных между экземплярами можно использовать более сложные события. В любом случае, дайте мне знать, что вы думаете, & если у вас есть другие предложения по зеркалированию состояния между различными точками входа!