State management has always been a cornerstone of building modern web applications, but let’s be honest—working with tools like Redux, MobX, or even React Query can often feel like overkill. Two years ago, I found myself frustrated with the endless boilerplate and complex setup these tools demanded.
The first annoying thing was the abount of boilerplate code I had to write just to start working with my state. Secondly, there was the challenge of connecting it to the application - installing multiple libraries, providing a context, using special hooks(not such a big problem), adding actions and reducers...
What could be simpler?
One library for everything, no boilerplate for implementation, no context providers and no hooks (🙄)
After a couple of experiments I created a simple library that worked as I wanted.
Well, what is nori-store
? It is the library that allows you to create separate states. Each state is independent and does not know about the others
To start working with nori-state
you need to install it and import it into a seperate module.
npm install nori-store
Secondly, import the library and create a state.
import { NoriState } from "nori-store";
export const UserState = new NoriState({
id: "unique_id",
firstName: "John",
secondName: "Doe",
age: 28,
});
console.log(UserState.value);
// {
// id: "unique_id",
// firstName: "John",
// secondName: "Doe",
// age: 28,
// }
Okay, the state is created. Nori-State provides the 'setValue' and 'set' methods to modify the state's data.
After modifying the data, we have to subscribe to the data changes. The subscriber is a function that triggers every time the state is modified.
UserState.setValue({ age: 29 });
// Or
UserState.set("age", 29);
const removeSubscriber = UserState.subscribe((state, prevState) => {
if (state.age === prevState.age) return;
// React only when the age has been changed
//
// Subscriber code...
});
removeSubscriber();
Okay, I can create a state, I can subscribe to the state's data changes. So, what's next? How does it work in a React application?
Well, in an existing React application, you just need to install nori-store
and create a file with a simple state that you intend to modify
// src/store/counterState.ts
import { NoriState } from "nori-store";
export const CounterState = new NoriState({
counter: 0,
});
In created component i have to import the state and use the data.
// src/App.tsx`
import { CounterState } from "./store/counterState";
export default function App() {
const buttonHandler = () => {
CounterState.set("counter", CounterState.value.counter + 1);
};
return (
<div>
<p>Counter: {CounterState.value.counter}</p>
{/* counter is 0 */}
<button onClick={buttonHandler}>increment</button>
</div>
);
}
In this case I am showing the counter value and modifying the state's data with a button handler. The state's data is changing, but it's only displaying the initial value. To display actual data i need to subscribe to the state and re-render the component each time the data changes.
// src/App.tsx`
import { useEffect, useState } from "react";
import { CounterState } from "./store/counterState";
export default function App() {
const [counter, setCounter] = useState(CounterState.value.counter);
useEffect(() => {
const deleteSubscriber = CounterState.subscribe((state, prevState) => {
if (state.counter === prevState.counter) return;
setCounter(state.counter);
});
return deleteSubscriber;
}, []);
const buttonHandler = () => {
CounterState.set("counter", CounterState.value.counter + 1);
};
return (
<div>
<p>Counter: {counter}</p>
{/* counter is 0 */}
<button onClick={buttonHandler}>increment</button>
</div>
);
}
I added the subscriber that triggers useState
hook to re-render component and display the actual counter value.
The code works, but it looks a bit bulky. To fix that, I need to add a hook that can hold all subscriber code inside (as a result, it is impossible without a hook 😏).
import { useEffect, useState } from "react";
import { NoriState } from "nori-store";
import { GeneralObjectType } from "nori-store/build/types/core/nori-state";
export function useNoriState<T extends GeneralObjectType>(
state: NoriState<T>,
...options: Array<keyof T>
) {
const [currentState, setCurrentState] = useState(state.value);
useEffect(() => {
return state.subscribe((value, prevValue) => {
const statedHasUpdated = options.some(
(option) => value[option] !== prevValue[option]
);
if (!options.length || (options.length && statedHasUpdated)) {
setCurrentState(value);
} else {
return;
}
});
}, [options, state]);
return currentState;
}
The universal hook is shown above. You can add any NoriState instance and subscribe to any property inside.
How does it change my component code?
// src/App.tsx`
import { CounterState } from "./store/counterState";
import { useNoriState } from "./hooks/useNoriState";
export default function App() {
const { counter } = useNoriState(CounterState, "counter");
const buttonHandler = () => {
CounterState.set("counter", counter + 1);
};
return (
<div>
<p>Counter: {counter}</p>
{/* counter is 0 */}
<button onClick={buttonHandler}>increment</button>
</div>
);
}
Conclusion
I use nori-store
in my projects for 2 years. It offers a simple, efficient, and flexible approach to state management that can work inside any project.
Unlike traditional libraries, it eliminates boilerplate code, context providers, and complex configurations, making it ideal for developers who value straightforward solutions. Whether you're building applications with React Native, Next.js, or Node.js, Nori-Store adapts seamlessly to your workflow. Its lightweight design ensures you can focus on what truly matters: creating great user experiences.
For more information about nori-store
and it's functionality check out the npm page or visit the GitHub repository for documentation, examples, and updates.
Top comments (0)