1724661756
2024-08-23 10:35:58
React開発では、アプリケーションの状態を外部データソース、特にLocalStorageなどのソリューションと同期させる必要があることがよくあります。React 18以降、新しいフックと呼ばれるものが追加されました。 useSyncExternalStore この同期を容易にするために導入されました。このフックを使用する具体的な例を説明します。
なぜ使うのか useSyncExternalStore?
useSyncExternalStore 複数のコンポーネント インスタンス間で共有される可能性のある外部ソースからデータを読み取り、同期するように設計されています。これにより、アプリケーションはこれらのデータ ソースの変更に効率的に反応し、状態の一貫性を確保して、スムーズなユーザー エクスペリエンスを提供できます。
具体的な例: アプリケーションテーマを LocalStorage と同期する
この例では、ユーザーが明るいテーマと暗いテーマを切り替えることができるReactアプリケーションを作成します。選択されたテーマはセッション間で保持されるようにLocalStorageに保存され、 useSyncExternalStore 異なるブラウザタブ間でも、アプリケーション全体でテーマの同期を管理します。
useThemeStore
最初のステップは、LocalStorage のリッスンと更新を処理するカスタム フックを作成することです。
import { useSyncExternalStore } from "react";
type Theme = "light" | "dark";
const THEME_STORAGE_KEY = "app-theme";
const getThemeFromLocalStorage = (): Theme => {
return (localStorage.getItem(THEME_STORAGE_KEY) as Theme) || "light";
};
const subscribe = (callback: () => void): (() => void) => {
window.addEventListener("storage", callback);
return () => {
window.removeEventListener("storage", callback);
};
};
const useThemeStore = (): [Theme, (newTheme: Theme) => void] => {
const theme = useSyncExternalStore(subscribe, getThemeFromLocalStorage);
const setTheme = (newTheme: Theme) => {
localStorage.setItem(THEME_STORAGE_KEY, newTheme);
window.dispatchEvent(new Event("storage"));
};
return [theme, setTheme];
};
export default useThemeStore;
- の
getThemeFromLocalStorageこの関数は、LocalStorage から読み取り、現在のテーマを取得する役割を担います。 - の
subscribeこの関数は、ブラウザのストレージ イベントを使用して、LocalStorage に加えられた変更をリッスンします。このメカニズムにより、変更が別のタブから発生した場合でも、アプリケーション内のテーマの状態を更新できます。 - ついに、
useThemeStoreこれはアプリケーション内で使用されるフックであり、現在のテーマの値を取得できます(theme) を変更したり (setTheme)。
ご覧の通り、useSyncExternalStore加入者(subscribe) と LocalStorage 読み取り関数 (getThemeFromLocalStorage) が正しく機能する必要があります。
応用
具体的な例を挙げてみます。 useThemeStore テーマを切り替えるためのフック ThemeToggler コンポーネント。このソリューションの利点は、すべてのブラウザ タブでテーマが変更されることです。
// Header.tsx
import React from "react";
import useThemeStore from "./useThemeStore";
import styled from "styled-components";
const StyledHeader = styled.header themeType: "light" | "dark" }>`
padding: 1rem;
text-align: center;
background-color: ${({ themeType }) =>
themeType === "light" ? "#f0f0f0" : "#222"};
color: ${({ themeType }) => (themeType === "light" ? "#000" : "#fff")};
`;
const Header: React.FC = () => {
const [theme] = useThemeStore();
return (
StyledHeader themeType={theme}>
h1>Current Theme: {theme.charAt(0).toUpperCase() + theme.slice(1)}h1>
StyledHeader>
);
};
export default Header;
// Footer.tsx
import React from "react";
import useThemeStore from "./useThemeStore";
import styled from "styled-components";
const StyledFooter = styled.footer themeType: "light" | "dark" }>`
padding: 1rem;
text-align: center;
background-color: ${({ themeType }) =>
themeType === "light" ? "#e0e0e0" : "#111"};
color: ${({ themeType }) => (themeType === "light" ? "#000" : "#fff")};
position: absolute;
bottom: 0;
width: 100%;
`;
const Footer: React.FC = () => {
const [theme] = useThemeStore();
return (
StyledFooter themeType={theme}>
p>Footer Content - Theme is {theme}p>
StyledFooter>
);
};
export default Footer;
// ThemeToggler.tsx
import React from "react";
import useThemeStore from "./useThemeStore";
import styled from "styled-components";
const ToggleButton = styled.button themeType: "light" | "dark" }>`
padding: 0.5rem 1rem;
font-size: 1rem;
cursor: pointer;
background-color: ${({ themeType }) =>
themeType === "light" ? "#000" : "#fff"};
color: ${({ themeType }) => (themeType === "light" ? "#fff" : "#000")};
border: none;
border-radius: 4px;
transition:
background-color 0.3s ease,
color 0.3s ease;
&:hover {
background-color: ${({ themeType }) =>
themeType === "light" ? "#333" : "#ddd"};
}
`;
const ThemeToggler: React.FC = () => {
const [theme, setTheme] = useThemeStore();
const toggleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
ToggleButton themeType={theme} onClick={toggleTheme}>
Switch to {theme === "light" ? "Dark" : "Light"} Theme
ToggleButton>
);
};
export default ThemeToggler;
// App.tsx
import React from "react";
import Header from "./Header";
import ThemeToggler from "./ThemeToggler";
import Footer from "./Footer";
import useThemeStore from "./useThemeStore";
import styled from "styled-components";
const AppContainer = styled.div themeType: "light" | "dark" }>`
min-height: 100vh;
background-color: ${({ themeType }) =>
themeType === "light" ? "#ffffff" : "#333333"};
color: ${({ themeType }) => (themeType === "light" ? "#000000" : "#ffffff")};
display: flex;
flex-direction: column;
align-items: center;
transition: all 0.3s ease;
position: relative;
padding-bottom: 3rem; // To ensure footer is visible
`;
const App: React.FC = () => {
const [theme] = useThemeStore();
return (
AppContainer themeType={theme}>
Header />
ThemeToggler />
Footer />
AppContainer>
);
};
export default App;
その他の具体的な使用例 useSyncExternalStore
- 接続ステータスの追跡: ユーザーの接続ステータス (ログインまたはログアウト) を追跡するために使用できます。たとえば、ユーザーが 1 つのタブでログアウトすると、この変更は開いている他のすべてのタブに自動的に反映されます。
- リアルタイムのデータ同期: API または WebSocket を介して、すべてのデータ ビューが最新バージョンの外部データと同期されることを保証できます。
- 通知管理: 異なるコンポーネントまたはブラウザ タブ間でグローバル通知を同期するために使用できます。
- 電子商取引アプリケーションでのショッピング カートの同期: 複数のコンポーネントまたはブラウザー タブ間でショッピング カートの内容を同期できます。
- …などなど。
結論
useSyncExternalStore は、アプリケーションの状態を LocalStorage などの外部データ ソースと同期するための強力なツールです。このフックを使用すると、複数のコンポーネント間、またはアプリケーションの複数のインスタンス間で共有データを処理できる React アプリケーションを作成できます。
使用することで useThemeStoreでは、LocalStorage を使用してアプリケーション テーマを保存および同期し、ページを再読み込みしたりタブを切り替えたりしてもスムーズで一貫したユーザー エクスペリエンスを確保する方法を説明しました。
出典と参考文献
#LocalStorage #で #useSyncExternalStore #を使用する例