State Manager
The store
variable is instance of the StateManger
class is designed to provide a robust and efficient way to manage the state within applications. It encapsulates functionality for state initialization, updates, listening for state changes, and persisting the state across sessions using local storage. With its singleton pattern, it ensures that only one instance of the state manager exists throughout the application, thus maintaining a centralized state management system.
Constructor
The constructor initializes the StateManager with an optional initial state. It prepares the state management system by setting up an internal state and a list of listeners for state changes.
-
Parameters:
initialState
- An optional initial state object for the StateManager. Defaults to an empty object if not provided.
Methods
getInstance(initialState)
A static method that ensures a singleton instance of the StateManager. It returns an existing instance or creates a new one with the provided initial state, also triggering the loadState method to initialize the state with any persisted data.
-
Parameters:
initialState
- An optional initial state for the new instance. Used only if an instance does not already exist. -
Returns: The
StateManager
instance.
setState(update)
Merges the provided updates into the current state, notifies all subscribed listeners about the change, and persists the updated state to local storage. This method allows for efficient state updates with immediate feedback to subscribed components.
-
Parameters:
update
- An object containing the updates to be merged into the current state.
subscribe(listener)
Subscribes a new listener function to state changes. The listener is immediately called with the current state upon subscription and provided with a function to unsubscribe.
-
Parameters:
listener
- The function to be called on state updates. - Returns: A function to unsubscribe the provided listener.
saveState()
Persists the current state to local storage, allowing the state to be retained across page reloads and browser sessions, thereby enhancing the user experience by maintaining application continuity.
loadState()
Loads the state from local storage, if available, and updates the state accordingly. This method is automatically called during the instance creation to ensure the state is initialized from persisted data if present.
resetState(id?: string)
Resets the state based on the provided id or clears the entire state if no id is provided. The updated state is also persisted in local storage.
-
With id: Removes only the specified part of the state (e.g.,
resetState("user")
resets theuser
key). - Without id: Resets the entire state to an empty object and clears the persisted state from local storage.
This is useful for scenarios such as user logouts (clear entire state) or refreshing specific parts of the state dynamically.
State Management for UI Components
This section illustrates how the StateManager
class can be applied to manage and persist the state of UI components, such as sidebar visibility and dropdown states, across page reloads. The example demonstrates how to integrate state management into UI interactions to enhance user experience by maintaining consistent UI states across sessions.
Overview
Proper management of UI component states, like a sidebar's open/close status or the expansion state of dropdown menus, significantly improves user experience by ensuring UI consistency. The following example showcases the use of StateManager
to achieve this goal efficiently.
Your First Store
To create a new store instance, you can use the global store
variable, which is available throughout the application. The following code snippet demonstrates how to create a new store instance with an initial state and subscribe to state changes. To observe the changes, open your developer console, navigate to the Application tab, and look for the key appState_59E13
in the local storage. This key represents the store's state saved in the local storage.
<button id="aside-button" class="p-2 bg-blue-500 text-white rounded">
Toggle Sidebar
</button>
<script>
const sideButton = document.getElementById('aside-button');
sideButton.addEventListener('click', () => {
store.setState({
sidebarOpen: !store.state.sidebarOpen
});
});
</script>
Open and Close Sidebar
<div class="relative z-50">
<button id="aside-button" class="p-2 bg-blue-500 text-white rounded fixed top-4 left-4">
Toggle Sidebar
</button>
</div>
<aside class="fixed top-0 left-0 w-64 h-full bg-gray-100 shadow transform -translate-x-full transition-transform z-40">
<div class="p-4">This is the sidebar content.</div>
</aside>
<script>
const aside = document.querySelector('aside');
const button = document.querySelector('#aside-button');
// Initialize sidebar state on page load
if (store.state.sidebarOpen) {
aside.classList.remove('-translate-x-full');
} else {
aside.classList.add('-translate-x-full');
}
// Button click event listener to toggle sidebar
button.addEventListener('click', () => {
store.setState({
sidebarOpen: !store.state.sidebarOpen
});
aside.classList.toggle('-translate-x-full', !store.state.sidebarOpen);
});
</script>
Managing Dropdown States
<div class="relative z-50">
<button id="dropdown-button" class="p-2 bg-blue-500 text-white rounded">
Toggle Dropdown
</button>
<div id="dropdown-menu" class="absolute w-48 bg-white border border-gray-200 shadow hidden">
<ul class="py-2">
<li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">Option 1</li>
<li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">Option 2</li>
<li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">Option 3</li>
</ul>
</div>
</div>
<script>
const dropdownButton = document.querySelector('#dropdown-button');
const dropdownMenu = document.querySelector('#dropdown-menu');
// Initialize dropdown state on page load
if (store.state.dropdownOpen) {
dropdownMenu.classList.remove('hidden');
dropdownMenu.classList.add('block');
} else {
dropdownMenu.classList.remove('block');
dropdownMenu.classList.add('hidden');
}
// Button click event listener to toggle dropdown
dropdownButton.addEventListener('click', () => {
store.setState({
dropdownOpen: !store.state.dropdownOpen
});
if (store.state.dropdownOpen) {
dropdownMenu.classList.remove('hidden');
dropdownMenu.classList.add('block');
} else {
dropdownMenu.classList.remove('block');
dropdownMenu.classList.add('hidden');
}
});
// Close dropdown if clicking outside
document.addEventListener('click', (event) => {
if (!dropdownButton.contains(event.target) && !dropdownMenu.contains(event.target)) {
store.setState({
dropdownOpen: false
});
dropdownMenu.classList.remove('block');
dropdownMenu.classList.add('hidden');
}
});
</script>
Persisting State Across Sessions
The examples above demonstrate effective strategies for saving and restoring UI component states using the StateManager
. This approach ensures that the sidebar's visibility and the state of dropdown menus are consistent across page reloads, thereby providing a seamless and engaging user experience.
Conclusion
Leveraging the StateManager
for UI state management enables developers to create more interactive and user-friendly web applications. By persisting UI states, applications can maintain user context and preferences across sessions, enhancing the overall usability and satisfaction.