-
Notifications
You must be signed in to change notification settings - Fork 0
context preference
The PreferenceContext provides a centralized way to manage and persist user preferences across the application. It wraps the app with a PreferenceProvider, exposing both the current preferences and a method to update them. Preferences are stored in localStorage, ensuring they persist across sessions and page reloads.
The PreferenceProvider now leverages TanStack React Query for managing preference state, loading, and mutation. This enables more robust data fetching, caching, and synchronization of preferences, especially in complex or asynchronous scenarios.
This context is ideal for managing UI-related settings such as theme selection, language, or layout density. It’s designed to be extensible, allowing additional preferences to be added without disrupting the existing structure.
type PreferenceContextType = {
userPreference: UserPreference;
updatePreferences: (preference: Partial<UserPreference>) => void;
};type UserPreference = {
theme: 'white' | 'g10' | 'g90' | 'g100';
[key: string]: unknown;
};const initialValue: UserPreference = {
theme: 'g10',
};import { usePreference } from '@/context/preference/usePreference';
const ThemeSelector = () => {
const { userPreference, updatePreferences } = usePreference();
const handleChange = (newTheme: string) => {
updatePreferences({ theme: newTheme });
};
return (
<select value={userPreference.theme} onChange={(e) => handleChange(e.target.value)}>
<option value="white">White</option>
<option value="g10">Gray 10</option>
<option value="g90">Gray 90</option>
<option value="g100">Gray 100</option>
</select>
);
};-
React Query Integration: The
PreferenceProviderusesuseQueryto load preferences anduseMutationto update them. This ensures preference state is always in sync and can be refetched or updated reactively. -
Persistence: Preferences are loaded from and saved to
localStorageusingloadUserPreferenceandsaveUserPreference, but now through React Query's data management. -
Extensibility: The
UserPreferencetype supports additional keys, making it easy to expand. -
Safety: The
usePreferencehook ensures it’s only used within a valid provider context, throwing an error otherwise.
-
useQuery({ queryKey: ['userPreference'], queryFn: loadUserPreference }): Loads preferences fromlocalStorageand keeps them in sync with the provider state. -
useMutation({ mutationFn: saveUserPreference }): Updates and persists preferences, triggering state updates and cache invalidation as needed. -
loadUserPreference(): Reads preferences fromlocalStorage, falling back to defaults if none exist. -
saveUserPreference(preference): Merges and stores updated preferences.
import { useQuery, useMutation } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import { PreferenceContext } from './PreferenceContext';
import { loadUserPreference, saveUserPreference } from './utils';
export const PreferenceProvider = ({ children }) => {
const [userPreference, setUserPreference] = useState(loadUserPreference());
const queryUserPreference = useQuery({
queryKey: ['userPreference'],
queryFn: loadUserPreference,
refetchOnMount: true,
});
const mutateUserPreference = useMutation({
mutationFn: async (updatedPreferences) => {
return Promise.resolve(saveUserPreference(updatedPreferences));
},
});
const updatePreferences = (preference) => {
const updatedPreferences = {
...userPreference,
...preference,
};
setUserPreference(updatedPreferences);
mutateUserPreference.mutate(updatedPreferences);
};
useEffect(() => {
if (queryUserPreference.isSuccess) {
setUserPreference(queryUserPreference.data);
}
}, [queryUserPreference]);
const contextValue = {
userPreference,
updatePreferences,
};
return <PreferenceContext.Provider value={contextValue}>{children}</PreferenceContext.Provider>;
};