API Reference
The useS hook offers a powerful yet minimal API to manage both local and global state in React — with built-in immutability and TypeScript support.
- State type is automatically inferred from the
initialValue. - Supported types:
null | undefined | number | string | boolean | bigint | function | asyncfunction | date | regexp | array | set | map | object.
🧠 useS(initialValue: T)
Creates a local state, just like useState, but with enhanced capabilities:
const [state, setState] = useS(0);🌍 useS({ value: T; key: string; })
Creates a global state shared across all components using the same key.
const [count, setCount] = useS({
value: 0,
key: "global-counter",
});This is functionally identical to local state, but allows cross-component sharing.
🔑 Global State Rules
keymust be a non-empty unique string.- The first call to
useS()with a given key sets the initial value. - All subsequent calls with the same key:
- Ignores the creation of a new state.
- Share the same state.
- This ensures state consistency without conflicts or duplication — regardless of where or how many times the key is used.
💿 Persistence { ..., persist?: bolean; }
Starting with version 2.3.0 of use-s-react, a third optional parameter was added to the global state configuration object called persist:
const [count, setCount] = useS({
value: 0,
key: "global-counter",
persist: true
});- When persist is set to true, you are telling useS to use the browser’s localStorage to read any initial value stored there and assign it to the initial value of the state that already exists, along with any valid changes, before creating the global state for the first time.
- In addition will save the change asynchronously in localStorage every time setState is executed without blocking the main thread.
- It will allow you, as a developer, to choose which global states you want to save and which you don’t.
- Facilitates debugging by using the same key for the global state in localStorage.
- This is a completely optional parameter and does not affect any previous functionality.
🧬 Built-In Immutability
- Immutability at entry: useS does not work on the memory reference of the initialValue, it only clones it and creates the state.
- Immutability at exit: useS returns a clone of the actual state, which can be mutated from the component, and this mutation does not affect the original state.
With useS, you will always respect React’s rule of not mutating state because it lets you do so without consequences and then allows you to use that mutation to update it with setState without risk:
const initialValue = new Set([1, 2, 3, 4]);
export function Component() {
const [mySet, setMySet] = useS(initialValue);
const handleAddItem = () => {
mySet.add(5); // mutating mySet state directly
setMySet(mySet); // setting the mutated state to generate a valid change
};
return (
<div>
<p data-testid="display">Items:{Array.from(mySet).join("-")}</p>
<button onClick={handleAddItem}>Add Item</button>
</div>
);
}
🔍 Intelligent Validation and Render Prevention
One of the most powerful optimizations in useS is its ability to prevent unnecessary re-renders through structural comparison between the previous and the next value.
This means that if the new value is not different or is not supported, the state will not be updated and the component will not be re-rendered.
How Does It Work?
Internally, useS uses a comparison function based on the data type.
Each supported type has its own mechanism to determine whether a change is valid:
| Value Type | Comparison Strategy |
|---|---|
| All | Object.is(a, b). |
Date | Compares timestamps using getTime(). |
RegExp | Compares source and flags. |
Set | Recursively compares each value. |
Map | Recursively compares each key and value. |
Array | Recursively compares each element. |
Object | Recursively compares keys and values, including nested ones. |
This allows you to handle complex structures without external libs or manual hacks like JSON.stringify.
⚠️ Exceptions
- Functions:
Although you can include functions as part of your state these are compared by reference. Every time a key is updated with a function value, it is likely to trigger a state update and re-render.
This behavior is consistent with how React handles functions.
- Initialization values:
null | undefined | [] | {} | new Set() | new Map()
The validation function recognizes the previous common initialization values, allowing them to be assigned to the state. In addition, if the previous value is one of these, it allows the next value to be assigned to the state, checking that it is a compatible value.
Summary
| Feature | Status | Notes |
|---|---|---|
| Local state | ✅ Complete | Same API as useState |
| Global state | ✅ Complete | Shared by key, no Provider required |
| TypeScript support | ✅ Complete | Fully inferred, no manual typing needed |
| Automatic immutability | ✅ Complete | Deep cloning at the entrance and exit |
| Avoid Unnecessary re-renders | ✅ Complete | Reference or deep value comparison with type validation |
| Derived state support | ✅ Complete | Supports computed state via function properties |
| React 18 compatibility | ✅ Complete | Uses useSyncExternalStore |
| Web & React Native | ✅ Complete | Compatible with both |
| Bundle size | ✅ Small | 22.9kB Unpacked Size |
| Boilerplate-free | ✅ Complete | No context, no Provider, no setup |
| Built-in persistence | ✅ Complete | Allows you to save and read the global state in localStorage |
| DevTools/debugging | ⚠️ Partial | debugGlobalStore() available via console |
Would you like to learn more?
Explore Hook Config to see how you can have complete control over what useS does under the hood.