Custom State Management
/ 6 min read
Last Updated:Hi guys.
Today we are going to build our custom state management inspired by SolidJS signal. It is very similar to React useState
.
- Part 1 - JSX and Virtual DOM
- Part 2 - Custom state management
- Part 3 - VirtualDOM: Optimize rendering process
Our Structure
We’ll build on the structure from our last post. Feel free to fork it, or start from scratch with a similar setup. Your final result should look like this:
Directorysrc
Directoryutils
- jsx-runtime.mjs
- render-jsx.mjs
- App.mjs
- index.html
- index.mjs
- .babelrc
- package.json
Intro to Signals
According to the official documentation.
State management is the process of managing the state of an application. This involves storing and updating data, as well as responding to the changes in it.
With Solid, state management is handled through signals and subscribers. Signals are used to store and update data, while subscribers are used to respond to changes in the data.
So, signals allow you to save data in a store and update it. When the data changes, it triggers the subscribers. Let’s adjust our App component a bit.
We have a simple component with a counter that increments when you click the button.
Currently, we’re getting an error because the createSignal function isn’t defined yet.
Custom createSignal Function
Alright, it’s time to define our own state management. First, let’s create a new file called signal.mjs
and place it in the ./src/utils
folder.
Directorysrc
Directoryutils
- jsx-runtime.mjs
- render-jsx.mjs
- signal.mjs new file!
- App.mjs
- index.html
- index.mjs
- .babelrc
- package.json
Next, let’s define the createSignal function.
So, we’ve defined a global state and the createSignal function. This function takes an initial value as an argument and returns a pair of functions: a getter and a setter, just like SolidJS’s createSignal function. It also skips updating if the new value is the same as the current state.
We’ve fixed the error, but clicking the Increment button still doesn’t do anything. The reason is that we’re not re-rendering the component after updating the state.
Re-render Function
We need to create a new file called render.mjs
in the ./src/utils/
folder.
Directorysrc
Directoryutils
- jsx-runtime.mjs
- render-jsx.mjs
- signal.mjs
- render.mjs new file!
- App.mjs
- index.html
- index.mjs
- .babelrc
- package.json
Next, we’ll define a render
function that will subscribe and re-render the component every time the state changes.
Let’s define the subscribeGlobal
function in signal.mjs
Let’s review what we’ve added. We implemented the subscribeGlobal function, which adds callbacks to the globalSubscribers variable and triggers each subscriber whenever the state updates. We’ll use subscribeGlobal to re-render our DOM.
We’re also utilizing queueMicrotask and isBatching to prevent multiple executions of flushBatch.
The microtask is a short function which will run after the current task has completed its work and when there is no other code waiting to be run before control of the execution context is returned to the browser’s event loop.
Now, when we run the project and click the increment button, the number increments as expected. Everything seems to be working, but it currently handles only one state. Let’s add another one.
There’s an issue: the second signal is overwriting the state of the previous one because both createSignal functions are using the same state.
Multiple States
To fix this, we need to extend the createSignal function to manage data in a separate state for each function. To prevent overwriting, we’ll use an index to store data at the correct position in the state. Let’s adjust our function accordingly.
So, what we have is that each time we call the createSignal
function, it increments globalStatePosition by 1. This helps us assign a unique position in the state for each createSignal
instance. We also reset the position to 0 whenever the page re-renders. Now everything works because each state is independent and doesn’t overwrite others.
Conclusion
We’ve built a simple state management system for components, inspired by the signal mechanism from SolidJS. This allows us to dynamically update data and render it on the page.
However, our custom state management has performance limitations. It needs improvements, such as re-rendering only specific components instead of the entire page and handling complex data. For now, it’s not suitable for production use.
Thank you for reading 😊