When it comes to state management in React applications, it can be difficult to choose the right library. There are quite a few solutions, each with its own features and advantages. In such a variety, choosing the best option becomes a real challenge. In this article we will talk about Valtio – a simple but powerful solution for state management in React.
Valtio offers a minimalistic and flexible approach to state management that is easy to learn and apply to many projects. Moreover, Valtio ranks 8th among state management libraries in terms of monthly downloads (npm) and 10th in GitHub stars (even surpassing some popular libraries such as Effector).
I want to share the benefits I’ve experienced in my work of using Valtio in React applications, and show how to effectively manage state using this library. We’ll also walk you through the core concepts and capabilities that Valtio provides—I’ll show you how to use them to create flexible and scalable state management in your projects.
Advantages of Valtio over other wealth management tools
When choosing a tool for state management in React, developers are faced with a variety of libraries and approaches. Valtio stands out among them with advantages that make it a powerful and attractive choice. How is Valtio different from other libraries?
- Ease of use: Valtio offers a simple and intuitive interface for working with status. It doesn’t require complicated setup or learning new concepts, making it easy to learn even for beginners. With simple, clean code, developers can quickly create and manage the state of their applications.
- Performance : Valtio provides efficient state management and optimized component rendering. Due to its reactive nature, Valtio updates components only when necessary, minimizing the number of unnecessary redraws. This allows you to create fast and responsive applications even when working with large amounts of data.
- Flexibility and Scalability: Valtio provides a flexible state management model that allows developers to create complex state hierarchies and easily scale them. Through the use of proxy objects, Valtio provides control over state changes and provides convenient access to the desired data.
- Integration with React and other tools: Valtio integrates seamlessly with React and other popular tools in the JavaScript ecosystem. You can use Valtio with React Hooks, context, React-suspense and others to create modern and powerful applications. In addition, Valtio is compatible with Redux DevTools, making it easy to monitor and debug application state.
- Active Community and Support: Valtio has a large community of developers who actively use and support this library. You can find extensive documentation, use cases and help in the community, making the process of learning and using Valtio more comfortable.
Valtio Basic Concepts
What makes Valtio unique is its mutation state. With it, you don’t need to use traditional methods like selectors and change comparison to identify and redraw components based on state.
The library provides a set of functions and capabilities that simplify the creation of proxy storage:
- proxy – in order to track changes to the original state and all its nested objects, notifying listeners about the change, it must be wrapped in a proxy:
import { proxy } from 'valtio';
const state = proxy({ count: 0, text: 'hello' });
The returned state object is mutable and can be modified like a regular JavaScript object:
setInterval(() => {
++state.count
}, 1000);
- snapshot – takes a state wrapped in a proxy and returns an immutable copy of it. This is useful when you want to pass the current state of your application to a component or function without allowing it to change the state directly. Immutability is achieved by effectively deep copying and freezing an object, allowing shallow comparison of rendering functions while preventing spurious re-rendering.
import { proxy, snapshot } from 'valtio';
const state = proxy({ count: 0, text: 'hello' });
const snap1 = snapshot(store);
const snap2 = snapshot(store);
console.log(snap1 === snap2); // true, no need to re-render
- useSnapshot – when using it, we get an immutable copy of the state that can be used to read data without the ability to change the original state. The advantage of using useSnapshot is that we can get the current state of the application at any point in time without having to keep track of changes and without having to create reactive communication. This makes it easier to read data from state and allows us to create components that rely only on a snapshot to display certain data, without causing re-renders when the rest of the state changes.
// Re-rendering will only occur when `state.count` changes, changes to `state.text` will not affect this
function Counter() {
const snap = useSnapshot(state);
return (
<div>
{snap.count}
<button onClick={() => ++state.count}>+1</button>
</div>
);
};
- useProxy – The utility works similarly to the useSnapshot method, but returns a shallow current state and a snapshot of it. So you can change it, but only at the root level and within the scope of the component.
import { proxy } from 'valtio';
import { useProxy } from 'valtio/utils';
const state = proxy({ count: 1 });
const Component = () => {
const $state = useProxy(state);
return (
<div>
{$state.count}
<button onClick={() => ++$state.count}>+1</button>
</div>
);
};
- subscribe is a method that allows you to access the state and subscribe to its changes outside the component. When called, you pass a handler function that will be called every time a change occurs in the state. The call also returns the unsubscribe method, which is appropriate to use when unmounting a component or another case when there is no need to monitor changes.
import { subscribe } from 'valtio';
// Подписка на все изменения состояния
const unsubscribe = subscribe(state, () =>
// Actions with state
);
// вызов метода отписки
unsubscribe();
You can subscribe to changes not only for the entire state, but also for part of it:
const state = proxy({ obj: { foo: 'bar' }, arr: ['hello'] });
subscribe(state.obj, () => console.log('state.obj has changed to ', state.obj));
state.obj.foo = 'baz';
subscribe(state.arr, () => console.log('state.arr has changed to ', state.arr));
state.arr.push('world');
The built-in utilities subscribeKey and watch have similar functionality:
subscribeKey is designed to subscribe to changes behind a primitive state value:
import { subscribeKey } from 'valtio/utils';
const state = proxy({ count: 0, text: 'hello' });
subscribeKey(state, 'count', (value) =>
// Action with value
);
- watch subscribes via getters. Unlike subscribe, it provides the ability to subscribe to several proxy objects (state objects) at once. Subscription occurs using the get function passed to the callback. Any changes to proxy objects will result in an immediate callback being called. It should be noted that when watch is initiated, the callback will be launched automatically, despite the fact that there are no changes in the state yet; this is necessary to establish initial subscriptions.
import { proxy } from 'valtio';
import { watch } from 'valtio/utils';
const userState = proxy({ user: { name: 'Ivan' } });
const sessionState = proxy({ expired: false });
watch((get) => {
// `get` adds `sessionState` to listened objects
get(sessionState);
const expired = sessionState.expired;
// или сразу использует их
const name = get(userState).user.name;
console.log(`${name}'s session is ${expired ? 'expired' : 'valid'}`);
});
// 'Ivan's session is valid'
sessionState.expired = true;
// 'Ivan's session is expired'
The watch method, when run, returns a cleanup function, which can be triggered automatically (each time the callback is called again) or when it is called directly. Additional functionality can be added to this cleanup method via return in the utility.
- Valtio supports React-suspense and will issue promises that you access inside the component’s render function. This eliminates the need for asynchronous operations, so you can access data directly while the parent component is responsible for loading state and error handling.
const state = proxy({ post: fetch(url).then((res) => res.json()) });
function Post() {
const snap = useSnapshot(state);
return <div>{snap.post.title}</div>;
};
function App() {
return (
<Suspense fallback="Loading...">
<Post />
</Suspense>
);
};
- ref – allows you to store objects in a state without tracking them. This can be useful if you have large nested objects with accessors that you don’t want to proxy. ref allows you to store these objects inside a state model. They will not be wrapped in an internal proxy and therefore will not be tracked.
import { proxy, ref } from 'valtio';
const state = proxy({
count: 0,
dom: ref(document.body),
});
- You can extract data from the state not only using the methods and utilities provided by the library, but also by simply destructuring the state object. In this case there will be no proxy wrapper and no state change subscription. You can read data in a component without calling a re-render.
function Foo() {
const { count, text } = state;
// ...
We have considered only the basic and frequently used methods and utilities of the library. However, Valtio offers even more features and functions that can be useful in various development scenarios. If you are interested in this state management tool, you can read its full documentation.
Bottom line
In conclusion, I can say that for me Valtio is a powerful and fast tool for managing state in React applications. In the article I showed the basic principles of the library and its key features.
One of the main advantages of Valtio is its approach to state mutation, which eliminates the need to use traditional methods such as selectors and change comparison. This allows us to create reactive state bindings and automatically update the UI when changes occur.
It is important to note that Valtio is one of the first libraries to successfully implement the proxy pattern in React applications, which provides convenience for mutating state and subscribing to state changes. Thanks to this, we get high performance and flexibility in state management.
About The Author: Yotec Team
More posts by Yotec Team