Note
This repository was moved into reactive-forms monorepo
Tiny state management library for react.
npm i stocked
This is tiny state management library, inspired by Recoil.
If you need to share state between your components, you'll face the performance issue. Saving all your app state into context isn't good solution, because changing value causes whole app rerender.
"Stocked" adds a little functionality for handling global state. Changing this state not rerenders all your app, it rerenders only components, which use it.
Wrap your app into StockRoot
component. To access stock variable, use useStockValue
hook. If you want to change variable, use useStockState
hook, which is similar to standard React's useState
hook.
import React from 'react';
import { StockRoot, useStockState } from 'stocked';
const initialValues = {
testValue: 'asdf',
};
const StockInput = () => {
const [value, setValue] = useStockState('hello');
return <input value={value} onChange={e => setValue(e.target.value)} />;
};
const App = () => (
<StockRoot initialValues={initialValues}>
<div>
<StockInput />
</div>
</StockRoot>
);
"Stocked" saves all values into React reference. As we know, changing value inside React reference not enforces component re-render. But why we can still get actual values? Because you can observe value change. So, what useStockValue
hook does, it just subscribes to value changes, and when value will change, it will change it's own state, what will force component to rerender.
StockRoot supports only 1 stock. This means, that when you use useStockState
hook, it will take stock from nearest parent context. To use few stocks at the same time, we suggest to create your own StockRoot
:
/* Declare types for your stock values */
type Stock1Values = {
test: string;
test2: string;
};
type Stock2Values = {
example: string;
nested: {
value: number;
};
};
/* Declare type for your context */
type MyStockContextType = {
stock1: Stock<Stock1Values>;
stock2: Stock<Stock2Values>;
};
/* Create context */
const MyStockContext = React.createContext<any>({});
/* Create your custom StockRoot */
type MyStockRootProps = {
initialValues1: Stock1Values;
initialValues2: Stock2Values;
children?: React.ReactNode;
};
const MyStockRoot = ({ initialValues1, initialValues2, children }: MyStockRootProps) => {
const stock1 = useStock({ initialValues: initialValues1 });
const stock2 = useStock({ initialValues: initialValues2 });
return <MyStockContext.Provider value={{ stock1, stock2 }}>{children}</MyStockContext.Provider>;
};
/* Use value from stock */
const ExampleComponent = () => {
const { stock1 } = React.useContext(MyStockContext);
const { stock2 } = React.useContext(MyStockContext);
/* works the same as before, just with custom-stock provided */
const valueFromFirstStock = useStockValue('test', stock1);
const valueFromSecondStock = useStockValue('example', stock2);
return (
<div>
{valueFromFirstStock}/{valueFromSecondStock}
</div>
);
};
/* Or, use stock state state */
const ExampleStateComponent = () => {
const { stock1 } = React.useContext(MyStockContext);
const { stock2 } = React.useContext(MyStockContext);
/* works the same as before, just with custom-stock provided */
const [valueFromFirstStock, setFirstValue] = useStockState('test', stock1);
const [valueFromSecondStock, setSecondValue] = useStockState('example', stock2);
return (
<div>
{valueFromFirstStock}/{valueFromSecondStock}
</div>
);
};
The main component, which should wrap all code, which uses stock values.
Creates stock and puts it in StockContext
.
Name | Type | Default | Description |
---|---|---|---|
initialValues | object | Initial stock values |
Hook, returns Stock
object from Context or throws error, if used outside StockContext
.
Hook, returns new Stock
object.
Name | Type | Default | Description |
---|---|---|---|
initialValues | object | Initial stock values |
Hook, returns tuple of value and value set action.
Returns actual value.
This means, this hook fires re-render each time value in stock was changed.
Similar to standard React's useState
hook.
Name | Type | Default | Description |
---|---|---|---|
path | string | Path to variable in stock, deeply gets value. Explanation | |
stock | Stock |
undefined | Optional parameter, if you want to work with custom stock, not received from context. |
[value: V, set: (value: V) => void]
Hook, which returns actual stock value. This means, it will update component each time when value in stock changes.
Name | Type | Default | Description |
---|---|---|---|
path | string | Path to variable in stock, deeply gets value. Explanation | |
stock | Stock |
undefined | optional parameter, if you want to work with custom stock, not received from context. |
value: V
Object, containing values and function to work with stock
Name | Type | Default | Description |
---|---|---|---|
values | Readonly<React.MutableRefObject<T>> |
Reference to actual values1 | |
observe | <V>(path: string, observer: Observer<V>) => void |
Register observer, which will be called when variable was updated | |
stopObserving | <V>(path: string, observer: Observer<V>) => void |
Remove observer | |
setValue | (path: string, value: unknown) => void |
Set stock value | |
setValues | (values: T) => void |
Set all stock values | |
isObserved | (path: string) => boolean |
Returns, if value is observed or not |
For changing value use setValue
and setValues
instead.
For accessing variable use useStockValue
or useStockState
or, if you want to provide custom logic, subscribe to changes via observe
and remember to do cleanup via stopObserving
.
Why it is so complicated? Because of performance issues, stock not updates directly
values, what will cause whole app re-render. Instead, it uses observers to re-render only necessary parts.
Function, callen when value was changed.
type Observer<V> = (message: V) => void
Footnotes
-
WARN: do not try to mutate those values, or use them for display. ↩