useSnap
The main hook for reading and subscribing to state changes in your components.
Reacting to State Changes
const state = {
name: 'Bob', age: 42,
};
const PersonDetails : FC = memo(() => {
const [name, age] = useSnap(state, state => [state.name, state.age]);
return (
<div>Name: {name}</div>
<div>Age: {age}</div>
);
});
useSnap
is the main way to achieve reactivity against your state. The first parameter is the state you wish to observe, and the second parameter is a selector function.
In the above example, by accessing person.name
and person.age
in the selector, the component will rerender ONLY when the name
or age
properties change on the person object.
Selectors
The selector parameter is special. When your component is mounted, this function is first used to record the properties you will be accessing from the subscribed state. This is done by passing an empty PathRecorder
object as the parameter to the selector and executing it to record what properties it accessed.
This means that you should not put any code that does any type of complex logic in the selector. After the initial selector is run and the properties its observing are recorded, the selector is then executed with your real state to read all the values you are selecting.
After a change is detected in your subscriptions, the output of the selection is cached and re-rendering only happens if the value return from your selector has changed. The value change check is an object reference comparison, so you should generally wrap your selector in an array of outputs if you plan on return object references from your selector.
There is an optional third parameter that allows you to pass a dependency list that will trigger a re-render and resubscribe to the object tree. This is helpful for when you have a value from your component params that you wish to use to access your data, such as an id in a Map
or array index.
Deep values
const [name] = useSnap(state, state => [state.person.friend.name]);
If any property value in the chain changes the component will re-render. This includes if state.person
is reassigned.
Subscribing to an array index
const [name] = useSnap(state, state => [state.people[0].name]);
In the above example, the array index is respected.
Depending on external properties
const ObserveArrayIndex : FC<{index: number}> = ({index}) => {
const [name] = useSnap(state, state => [state.people[index].name], [index]);
}
The selector depends on an external parameter that is not on the state object. To make sure it resubscribes, pass in the parameters as a dependency like in typical react hooks
Selector Paths
The selector function can include pattern functions that allow you to control how you are subscribing to properties.
all()
const [person] = useSnap(state, state => [all(state.person)]);
Subscribes to all property changes on the person object only. Will re-render if any property on the person object changes.
const [value] = useSnap(state, state => [all(state.person).friend.name]);
Note that you can also make complicated chains. Here, any property change on state.person
will trigger a re-render as well as the subproperty friend.name
.
elements()
const state = {
people: [
{ name: 'Bob', age: 42 },
{ name: 'Alice', age: 40 }
];
}
const ObserveAllArrayChanges : FC = React.memo(() => {
const [people] = useSnap(state, state => [elements(state.people)]);
});
Subscribes to the collection as a whole and will re-render if an item is added, removed, or replaced.
Array
, Map
, and Set
are supported.
Note the selector is wrapped in an array. This is intentional since this selector returns a reference to the people object and selectors memoize output. Without the wrapping array, the CollectionComponent
would not re-render.
Note: if a property on a Person
object inside the people
array changes, it will not trigger a re-render of the component. This is by design.
map_get()
const state = {
myMap = new Map([['wizard', 'Merlin']]);
}
const MapGetter : FC = () => {
const [theWizard] = useSnap(state, state => [map_get(state.myMap, 'wizard')]);
}
Subscribes to individual Map
keys and will call Map.get
on your state.
Usage
const [name, age] = useSnap(person, person => [person.name, person.age]);
Return Value
Returns the result of running your selector on the supplied state.
Parameters
state
The state you wish to observe.
selector
A function that both describes the state that will be subscribed to as well as copies the state that used in your React components.
deps : any[]
(optional)
A standard React hooks dependency list will trigger a resubscribe to the object tree. This is helpful for when you have a value from your component params that you wish to use to access your data, such as an id in a Map
or array index.