State management
In order for layers to communicate with each other, they require a method for passing state between them. Additionally, the re-rendering cycle for state per layer needs to be managed, and there may also be a need for passing state between sibling components or other external actors.
Although a variety of state libraries can be utilised with the Unless Framework, it already includes the built-in StoreStack
.
Global state management
In some cases, you might need to manage state that is shared across multiple components, layers, or even external actors. Global state management can help you handle these scenarios efficiently. Although the Unless Framework is compatible with various state libraries, it includes the built-in StoreStack
for convenience.
Observing a store
To observe a store, create an observer using the useObserver
function. This function accepts a callback that will be called when the store's state changes:
const observer = useObserver((currentState, prevState) => {
console.log('State changed from', prevState, 'to', currentState);
});
Then, attach the observer to the store using the store.attach
method:
store.attach(observer);
You can also detach an observer from a store using the store.detach
method:
store.detach(observer);
Using global observers
If you want to use global observers that apply to all stores, you can use the StoreStack.attach
method:
const globalObserver = useObserver((currentState, prevState) => {
console.log('Global observer:', currentState, prevState);
});
Stores.addGlobalObserver(globalObserver);
Using useState
useState
useState
is the preferred method for managing state in StoreStack. It is a convenience method that combines creating, accessing, and updating a store. It is similar to the useState
hook in React:
const [getState, setState] = Stores.useState({
pointer: 'myPointer',
defaultValue: 0,
});
console.log(getState()); // Output: 0
setState((prevState) => prevState + 1);
console.log(getState()); // Output: 1
Connecting store(s) to the component rendering cycle.
Rendering Performance
To optimise performance, it's recommended not to use a global observer for the
Stores
to re-render the component. This is because re-rendering could be triggered by events that are outside the component scope, and outside of its relevant stores.
To connect a store to the rendering cycle, we must attach an observer in the constructor of the layer with the requestUpdate
.
For example:
constructor(...) {
super(...)
Stores.get(pointer)?.attach(
useObserver(() => this.requestUpdate())
)
}
This of course assumes that this.requestUpdate()
exists, which is the case for Display
and Layout
. For Element
, this must be manually passed down.
Layer level management
Display
and Layout
components have built-in state management at the layer level. This can be accessed through this.state
, which is an instance of StoreStack
that is isolated to the layer and pre-configured with a global observer that triggers re-rendering on change.
For more information on the specific API, see the StoreStack
documentation.
To implement similar behavior in an Element
, follow the steps below:
Step 1: Pass down the requestUpdate
hook
requestUpdate
hookFirst, pass down the requestUpdate
hook to the Element
component. This allows the Element
component to trigger a re-render when the state changes.
interface MyElementProps extends ElementProps {
updateHook: (...args: any[]) => void;
}
Step 2: Instantiate an instance of StoreStack
StoreStack
In the Element
component, instantiate an instance of StoreStack
and attach an observer that calls the updateHook
function when the state changes:
export class MyElement extends Element {
private state: StoreStack;
constructor(props: MyElementProps) {
super(DefaultElementSettings);
this.state = StoreStack.attach([useObserver(() => props.updateHook())]);
}
...
}
Now, when the state changes in the Element
component, the updateHook
function will be called, and the parent component will re-render accordingly.
By following these steps, you can implement layer-level state management in your Element
components using the StoreStack
library. This provides a consistent and efficient way to manage state and re-render components when necessary.
Updated 6 months ago