State Management
States are some internal values maintained by an element.
These are important because if these values change, the element gets re-rendered.
You can refer to them in code, like in innerHTML
, css
or any other property of the element. On change, the element automatically updates their reference in the code.
$state
Method
In SpuckJs, we use the state using $state
method.
$state
method returns a function to update that state.
NOTE: State can only be managed in a rendered element.
Let's see how it works.
/*
Lets create a new display element.
Render it while initializing because
State can only be managed in a rendered element.
*/
const Display = new Spuck({ type: 'h3', parent: '#app', id: 'dis' }).render();
const setCount = Display.$state('count', 0);
First Parameter: name of state
Second Parameter: initial value of state
Now to refer this state in the code, there are two ways:
.getState
method
When you refer to the state value using this method, it will not update the reference in the code when state changes.It can only update if the line of code where this method is mentioned runs again.
That is, if used in a loop, bind to an event or used in an effect.
In all these cases, the state value will be updated when the line of code runs again.
$-
reference
If you truly want to see the state reference update in the code, you use $-
reference.
Syntax:
$-stateName
NOTE: $-
can only be used in internal properties of the element. You can't do operations using this, { text: '$-count + 1' }
.
Back to the example.
we are done withDisplay
element, so we make
it. We'll update the count using another element : Button
setState
function
If you want to update any state of the element, you use the method returned by $state
method.
By convention, we call this method set{StateName}
When you call it, it automatically re-renders the element after updating the state.
We pass a new value of state to this method.
There can be two ways for it:
Direct
Functional
Direct way
In this case, we pass the latest state value directly as parameter.
// suppose we want to update color of the element
setState('red');
// suppose we want to increment any "Click State" by 1
setClick(Element.getState('click') + 1);
// see how we "used" the "getState" method
Functional way
Here, we pass state value with respect to the previous state value as parameter using a functional
approach
We call a function as parameter, whose argument is the previous state value.
Then we return a new value for state by manipulating previous one.
// suppose we want to update the state to double the previous value
setState(function(previousState) {
return previousState * 2;
});
// this can be simplified using arrow function
setState(prevState => prevState * 2);
// see how we "ignored" the "getState" method
⠀
Back to our Display
example, we can use setState
to update count on a button click
/*
Defining a button, we don't necessarily need to render
it at this point cause there is no state of the "Button"
to manage.
*/
const Button = new Spuck({ type: 'button', parent: '#app', id: 'btn' });
Button.prop = { text: 'Update Count' };
Button.events = {
// on click, update count using functional approach
click: () => setCount(prevCount => prevCount + 1)
};
Button.make();
Display
at once:
const Display = new Spuck({ type: 'h3', parent: '#app', id: 'dis' }).render();
const setCount = Display.$state('count', 0);
Display.prop = { text: 'Count is $-count' };
Display.make('re');
const Button = new Spuck({ type: 'button', parent: '#app', id: 'btn' });
Button.prop = { text: 'Update Count' };
Button.events = {
click: () => setCount(prevCount => prevCount + 1)
};
Button.make();
State Sharing
In SpuckJs you can easily share states to other elements.
You can share states to siblings, parent, children or any other elements, it does not matter.
Pseudo Children/States
SpuckJs does it by using a concept called pseudo-Children
.
If you want to share state of Element 1
with Element 2
, you can do it by making "Element 2" a pseudo-Child
of "Element 1".
const Element1 = new Spuck({ ... }).render();
const Element2 = new Spuck({ ... }).render();
Element1.init.pseudoChildren = [Element2];
Element1.render('re');
pseudoChildren
is a property of init
object of the element.It is an array, and can hold multiple elements.
Now all the states of Element 1
are accessible to Element 2
as pseudo-states
.
NOTE: When an element renders, all its pseudo-children are also rendered.
Referencing them
Just like normal states, you can refer to pseudo-states
either using:
.getPseudoState
method, or
$$-
reference
NOTE: $-
: states and $$-
: pseudo-states
Extending "Display" Example
Suppose there is one more element, say Display2
, below the Button
, which shows the count of the Display
element.
We will make Display2
a pseudo-child of Display
, to access the count
state.
const Display2 = new Spuck({ type: 'h3', parent: '#app', id: 'dis2' });
Display1.init.pseudoChildren = [Display2];
Display1.render('re');
count
state of Display
from Display2
as pseudo-state.
Looking at the Object
If we console log the Display
element now, we can see how are properties managed.
You can see states are stored in _state
property of the element.
Each state is an array
=> [value, setter]
Display
: pseudo-child : Display2
It contains the whole virtual element of Display2
in the pseudoChildren
array.
⠀
Looking at Display2
It does not differ much, but instead of _state
property, its _pseudoState
property is defined, which has the same structure as _state
.
You can see the el
property of both elements, that is the physical element stored within the virtual element.
If we console log Display.el
, we can see the HTML element:
Its there if you ever have to interact with the browser api, you can use it.