Custom State Management System
Mutables are not developed to use with lints, to solve this problem we create an approach that solves this.
Purpose
Mutables will always impact DOM because setMutable tries to locate constructos that need to refresh. To avoid this mechanism, create your own state management system that can split resources between store simple data and update shadow DOM.
Code
In below code we define an ephemeral memory that will store our data and we create get and set methods to access this data. To store persistent data we will use localstorage.
export class __StateManagement__ {
constructor() {
/**@private */
this.ephemeralMemory = [];
}
/**
* Print on console a report about state management actual stored data
* @param {boolean} ephemeral true: show ephemeral data, false: show localstorage data
*/
PRINT_STATE(ephemeral) {
const printEphemeral = () => {
console.warn("EPHEMERAL STATE MANAGEMENT DATA REPORT");
console.log(this.ephemeralMemory);
};
const printLocalStorage = () => {
let total = localStorage.length;
let res = [];
for (let c = 0; c < total; c++) {
let key = localStorage.key(c);
if (key.includes("sm_")) {
res.push({
key,
value: localStorage.getItem(key),
});
}
}
//
console.warn("LOCALSTORAGE STATE MANAGEMENT REPORT");
console.table(res);
};
ephemeral ? printEphemeral() : printLocalStorage();
}
/**@private */
del(key) {
localStorage.removeItem("sm_" + key);
}
/**@private */
set(key, value) {
localStorage.setItem("sm_" + key, value);
}
/**
*
* @param {string} key
* @param {string|Number|event|Array|Boolean} value
*/
set_ephemeral(key, value) {
this.ephemeralMemory[key] = value;
}
/**@private */
get(key) {
return localStorage.getItem("sm_" + key);
}
/**@private */
get_ephemeral(key) {
return this.ephemeralMemory[key];
}
}
This JavaScript class, __StateManagement__
, is a simple state management system that offers two storage options: ephemeral (temporary) and persistent (localStorage). Let’s go through the code to see how each part works.
Class: __StateManagement__
The class has two main storage mechanisms:
ephemeralMemory
: An in-memory array for temporary data.localStorage
: Persistent storage using the browser’slocalStorage
API, which keeps data even after page reloads.
Constructor
constructor() {
/**@private */
this.ephemeralMemory = [];
}
This sets up an empty array ephemeralMemory
to store temporary data. The @private
comment indicates that it’s for internal use only.
Method: PRINT_STATE
PRINT_STATE(ephemeral)
Purpose: Logs a report to the console, showing either the ephemeral data or the data in localStorage
, depending on the ephemeral
parameter.
Parameter: ephemeral
(boolean) - If true
, it prints ephemeral data. If false
, it logs localStorage
data.
Helper Functions within PRINT_STATE
printEphemeral
: Logs the current ephemeral data stored inthis.ephemeralMemory
.printLocalStorage
: Loops throughlocalStorage
items with keys that start with"sm_"
, and displays them in a table.
Method: del
del(key)
Purpose: Deletes an item from localStorage
based on the given key
.
Parameter: key
(string) - The identifier for the item to delete.
Method: set
set(key, value)
Purpose: Stores an item in localStorage
.
Parameters:
key
(string) - The identifier for the item.value
(string) - The value to store.
Method: set_ephemeral
set_ephemeral(key, value)
Purpose: Adds or updates an item in ephemeralMemory
.
Parameters:
key
(string) - The identifier for the item.value
(string|number|event|array|boolean) - The value to store.
Method: get
get(key)
Purpose: Retrieves an item from localStorage
using the given key
.
Parameter: key
(string) - The identifier for the item.
Method: get_ephemeral
get_ephemeral(key)
Purpose: Retrieves an item from ephemeralMemory
using the specified key
.
Parameter: key
(string) - The identifier for the item.
This class provides a structured way to manage both temporary and persistent state data, making state retrieval and debugging easy with the PRINT_STATE
method.
Now we can create our own class that extends those methods. In MyManager, we can create any methods we need to control out application.
import {Winnetou} from 'winnetoujs';
import {__StateManagement__} from './stateManagementMainClass';
class MyManager extends __StateManagement__{
constructor(){
super()
}
// put here your implementations
device = {
set:
/**
*
* @param {"android"|"pc"|"mobile"} value
* @returns
*/
value => this.set_ephemeral("device", value),
get:
/**
*
* @returns {"android"|"pc"|"mobile"}
*/
() => this.get_ephemeral("device"),
isAndroid: () => this.get_ephemeral("device") === "android",
isMobile: () => this.get_ephemeral("device") === "mobile",
isPc: () => this.get_ephemeral("device") === "pc",
};
}
This code defines a class, MyManager
, which extends __StateManagement__
and provides additional functionality for managing device types within a state management system. Let’s break down each part of the code.
Imports
import { Winnetou } from 'winnetoujs';
import { __StateManagement__ } from './stateManagementMainClass';
This imports two modules:
Winnetou
from thewinnetoujs
package.__StateManagement__
from a local module, whichMyManager
will extend.
Class: MyManager
class MyManager extends __StateManagement__ {
constructor() {
super();
}
}
This class extends __StateManagement__
, inheriting its state management methods (such as set_ephemeral
and get_ephemeral
). The constructor
calls super()
to ensure the base class is initialized.
Property: device
The device
property in MyManager
defines a set of methods for managing a "device" type within the ephemeral state. The possible values are "android"
, "pc"
, or "mobile"
. Let’s go over each method.
device.set
set:
/**
*
* @param {"android"|"pc"|"mobile"} value
* @returns
*/
value => this.set_ephemeral("device", value),
Purpose: Sets the device type in the ephemeral memory.
Parameter: value
- Can be "android"
, "pc"
, or "mobile"
.
This method calls this.set_ephemeral("device", value)
to store the device type temporarily.
device.get
get:
/**
*
* @returns {"android"|"pc"|"mobile"}
*/
() => this.get_ephemeral("device"),
Purpose: Retrieves the current device type from ephemeral memory.
Returns: The stored device type, which can be "android"
, "pc"
, or "mobile"
.
device.isAndroid
isAndroid: () => this.get_ephemeral("device") === "android",
Purpose: Checks if the stored device type is "android"
.
Returns: true
if the device type is "android"
, otherwise false
.
device.isMobile
isMobile: () => this.get_ephemeral("device") === "mobile",
Purpose: Checks if the stored device type is "mobile"
.
Returns: true
if the device type is "mobile"
, otherwise false
.
device.isPc
isPc: () => this.get_ephemeral("device") === "pc",
Purpose: Checks if the stored device type is "pc"
.
Returns: true
if the device type is "pc"
, otherwise false
.
The MyManager
class extends __StateManagement__
to manage device-specific states in ephemeral memory. The device
property allows setting, getting, and checking device types, making it a simple and structured way to manage device information within the state management system.
Updating Constructos Using Mutables
// somewhere in code ...
MyManager.profile.stars.create()
profileCard({
name:'John',
stars: {mutable:'stars'}
})
MyManager.profile.stars.add()
// it updates profileCard constructo automatically
// inside MyManager class
// ...
profile = {
stars = {
create: () => !Winnetou.getMutable('stars') &&
Winnetou.setMutable('stars','0'),
add: () => {
let actualStars = parseInt(Winnetou.getMutable('stars')) || 0;
actualStars++;
Winnetou.setMutable('stars',actualStars);
},
remove: () => {
let actualStars = parseInt(Winnetou.getMutable('stars')) || 0;
actualStars--;
actualStars < 0 && (actualStars = 0);
Winnetou.setMutable('stars',actualStars);
}
}
}
This code expands the MyManager
class to manage a "stars" property within a user profile. The "stars" value can be created, added to, or removed, and it interacts with an external UI component in real-time. Here’s how it works.
Code Usage Example
// somewhere in code ...
MyManager.profile.stars.create();
profileCard({
name: 'John',
stars: { mutable: 'stars' }
});
MyManager.profile.stars.add();
// it updates profileCard construct automatically
This example shows how MyManager.profile.stars
methods are used to create and update a "stars" counter associated with a profile. When the add()
method is called, it automatically updates the profileCard
UI to reflect the new stars count.
Property: profile
in MyManager
The profile
property defines a nested stars
object, which contains methods to manage a user's "stars" count. The "stars" value is stored as a mutable state, accessible via the Winnetou
library.
stars.create
create: () => !Winnetou.getMutable('stars') &&
Winnetou.setMutable('stars', '0')
Purpose: Initializes the stars count if it doesn't exist yet. Checks if the "stars" mutable value exists; if not, it sets it to '0'
.
stars.add
add: () => {
let actualStars = parseInt(Winnetou.getMutable('stars')) || 0;
actualStars++;
Winnetou.setMutable('stars', actualStars);
}
Purpose: Increments the "stars" count by 1.
How it works: Retrieves the current "stars" value, parses it as an integer, increments by 1, and updates it in the mutable state. This change is reflected in any UI bound to stars
, such as profileCard
.
stars.remove
remove: () => {
let actualStars = parseInt(Winnetou.getMutable('stars')) || 0;
actualStars--;
actualStars < 0 && (actualStars = 0);
Winnetou.setMutable('stars', actualStars);
}
Purpose: Decrements the "stars" count by 1, but prevents it from going below zero.
How it works: Gets the current "stars" count, decreases it by 1, and ensures it doesn’t drop below 0. Updates the mutable state, automatically reflecting the change in any UI element bound to "stars."
This code provides a mechanism to manage a user's "stars" count within the MyManager
class. The stars
property inside profile
allows for creating, incrementing, and decrementing the stars value. Using Winnetou
to manage mutable state enables real-time updates, making it easy to sync the "stars" count with UI elements like profileCard
.