Skip to content

Commit 74331fb

Browse files
committed
refactor: migrate state management to new state-manager module with persistence support
1 parent 192a744 commit 74331fb

1 file changed

Lines changed: 53 additions & 94 deletions

File tree

public/js/components/state-example.js

Lines changed: 53 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,55 @@
22
* Example component demonstrating state manager integration
33
*/
44
console.log('Loading state-example.js');
5-
import { createStore, StoreConnector } from '../deps.js';
6-
console.log('Imported createStore and StoreConnector from deps.js');
5+
import { createStateManager, createConnectedComponent } from '../modules/state-manager/dist/index.js';
6+
console.log('Imported createStateManager from state-manager module');
77

8-
// Create a store for this component
9-
const store = createStore('stateExample', {
8+
// Create a state manager with initial state
9+
const stateManager = createStateManager({
1010
counter: 0,
1111
theme: document.documentElement.getAttribute('data-theme') || 'light',
1212
user: { loggedIn: false }
13+
}, {
14+
enablePersistence: true,
15+
persistenceKey: 'state_example',
16+
debug: true
1317
});
1418

15-
// Define a base component class
16-
class StateExampleBase extends HTMLElement {
19+
// Define the component class
20+
class StateExampleElement extends HTMLElement {
1721
constructor() {
1822
super();
23+
console.log('StateExampleElement constructor called');
1924
this.attachShadow({ mode: 'open' });
2025
}
2126

2227
connectedCallback() {
23-
// Connect to state keys
24-
this.connectToState(['counter', 'theme', 'user']);
25-
console.log('Connected to state keys: counter, theme, user');
28+
console.log('StateExampleElement connected to DOM');
29+
this.render();
30+
}
31+
32+
/**
33+
* This method will be called when state changes
34+
* @param {object} state - The current state
35+
* @param {string} path - The path that changed
36+
* @param {object} fullState - The full state object
37+
*/
38+
stateChanged(state, path, fullState) {
39+
console.log('State changed in component:', path);
40+
console.log('New state:', fullState);
41+
console.log('State value:', state);
2642

43+
// Re-render the component
2744
this.render();
28-
this.addEventListeners();
45+
46+
// If the theme changed, update the document theme
47+
if (path === 'theme') {
48+
document.documentElement.setAttribute('data-theme', state);
49+
}
2950
}
3051

3152
render() {
32-
// Get current counter value from state
53+
// Get current values from state
3354
const counter = this.getState('counter') || 0;
3455
const theme = this.getState('theme') || 'light';
3556
const user = this.getState('user') || { loggedIn: false };
@@ -128,6 +149,9 @@ class StateExampleBase extends HTMLElement {
128149
</div>
129150
</div>
130151
`;
152+
153+
// Add event listeners after rendering
154+
this.addEventListeners();
131155
}
132156

133157
addEventListeners() {
@@ -139,11 +163,14 @@ class StateExampleBase extends HTMLElement {
139163
console.log('Increment button clicked');
140164
const currentCounter = this.getState('counter') || 0;
141165
console.log('Current counter value:', currentCounter);
142-
this.setState({ counter: currentCounter + 1 });
143-
console.log('Counter incremented to:', currentCounter + 1);
166+
167+
try {
168+
this.setState({ counter: currentCounter + 1 });
169+
console.log('Counter incremented to:', currentCounter + 1);
170+
} catch (error) {
171+
console.error('Error setting state:', error);
172+
}
144173
});
145-
} else {
146-
console.warn('Increment button not found in the shadow DOM');
147174
}
148175

149176
this.shadowRoot.getElementById('decrement')?.addEventListener('click', () => {
@@ -161,15 +188,6 @@ class StateExampleBase extends HTMLElement {
161188
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
162189

163190
this.setState({ theme: newTheme });
164-
165-
// Also update document theme for the whole application
166-
document.documentElement.setAttribute('data-theme', newTheme);
167-
168-
// Dispatch theme change event for other components
169-
const event = new CustomEvent('themechange', {
170-
detail: { theme: newTheme }
171-
});
172-
document.dispatchEvent(event);
173191
});
174192

175193
// User login/logout
@@ -178,84 +196,25 @@ class StateExampleBase extends HTMLElement {
178196

179197
if (user.loggedIn) {
180198
// Logout
181-
this.setState({
182-
user: { loggedIn: false }
183-
});
199+
this.setState({ user: { loggedIn: false } });
184200
} else {
185201
// Login (simulate)
186-
this.setState({
187-
user: {
188-
loggedIn: true,
189-
username: 'DemoUser',
202+
this.setState({
203+
user: {
204+
loggedIn: true,
205+
username: 'DemoUser',
190206
email: 'demo@example.com',
191207
lastLogin: new Date().toISOString()
192-
}
208+
}
193209
});
194210
}
195211
});
196212
}
197-
198-
// This method will be called when state changes
199-
stateChanged(state, changedKeys) {
200-
console.log('State changed in component:', changedKeys);
201-
console.log('New state:', state);
202-
203-
// Check if the counter key changed
204-
if (changedKeys.includes('counter')) {
205-
console.log('Counter changed, updating display directly');
206-
207-
// Get the counter display element
208-
const counterDisplay = this.shadowRoot.querySelector('.counter-display');
209-
if (counterDisplay) {
210-
// Update the counter display directly
211-
// Handle both cases: when state is the full state object or just the counter value
212-
let counterValue;
213-
if (typeof state === 'object' && state !== null) {
214-
// If state is an object, it's the full state object
215-
counterValue = state.counter;
216-
console.log('State is an object, counter value:', counterValue);
217-
} else {
218-
// If state is not an object, it's the counter value itself
219-
counterValue = state;
220-
console.log('State is the counter value itself:', counterValue);
221-
}
222-
223-
counterDisplay.textContent = `Counter: ${counterValue}`;
224-
console.log('Counter display updated directly to:', counterValue);
225-
console.log('Counter display element text after update:', counterDisplay.textContent);
226-
} else {
227-
console.error('Counter display element not found');
228-
// If the element doesn't exist, do a full render
229-
this.render();
230-
}
231-
232-
// Also update the state display
233-
const stateDisplayPre = this.shadowRoot.querySelector('.state-display pre');
234-
if (stateDisplayPre) {
235-
// Get the full state from the state manager
236-
const fullState = this.getState();
237-
stateDisplayPre.textContent = JSON.stringify(fullState, null, 2);
238-
}
239-
} else {
240-
// For other state changes, do a full render
241-
console.log('Other state changed, doing full render');
242-
this.render();
243-
244-
// Re-attach event listeners after render
245-
console.log('Re-attaching event listeners after render');
246-
this.addEventListeners();
247-
}
248-
}
249213
}
250214

251-
// Create the connected component using the StoreConnector
252-
const StateExample = StoreConnector(store)(StateExampleBase);
253-
254-
// Register the custom element
255-
customElements.define('state-example', StateExample);
256-
257-
// Initialize default state if not already set
258-
// No need for DOMContentLoaded event handler as we're using a local store
259-
// that's already initialized with default values
215+
// Register the component with the state manager
216+
createConnectedComponent('state-example', StateExampleElement, {
217+
statePaths: ['counter', 'theme', 'user']
218+
}, stateManager);
260219

261-
export default StateExample;
220+
export default StateExampleElement;

0 commit comments

Comments
 (0)