README
Reactive variables
Observable values for React. For observable dictionaries for React, take a look at @yanfoo/react-dict
. This project was inspired by jorbuedo's react-reactive-var
.
Breaking changes
1.2.0 > 1.3.0
- removedReactVar.isLocked
andReactVar.wait()
in favor of sequential modifications.
Install
npm install @yanfoo/react-var --save
yarn add @yanfoo/react-var
Usage
import { createReactVar, useReactVar } from '@yanfoo/react-var';
// default comparator is equality : a === b
const activeUser = createReactVar(null, { comparator: (a, b) => a?.id === b?.id });
export const authenticator = {
login: (username, password) => {
activeUser({ id: 123, username });
},
logout: () => {
activeUser(null);
}
};
export const useAuthenticatedUser = () => useReactVar(activeUser);
Limitations
Setting a new values to a ReactVar
can be made asynchronously. Meaning that any
modification will cause parallel modifications to be stacked and processed
sequentially. However, trying to modify a ReactVar
within a subscriber callback
will fail, and this is in order to prevent race conditions, where the subscriber
and the setter would wait for each other to complete, creating a dead lock situation.
API
declare type ReactVarChangeEvent<T> = {
/**
* The current value
*/
value: T
}
type ReactVar<T> = {
/**
* Set a new value and optionally notify subscribers. If the new value
* is a function, or an asynchronous function, it will be called asynchronously
* with the current value as argument.
*/
(newValue?: T | ((value: T) => T) | ((value: T) => Promise<T>)): Promise<ReactVar<T>>;
/**
* Get the current value
*/
readonly value: T;
/**
* Register a new subscriber. Returns a function to unsubsribe the handler
* @param handler will be called with the updated value
*/
subscribe(handler: (event: ReactVarChangeEvent<T>) => Promise<void> | void): () => void;
/**
* Unregister a subscriber
* @param handler must be the exact same subscribed handler
*/
unsubscribe(handler: (event: ReactVarChangeEvent<T>) => Promise<void> | void): void;
}
type ReactVarOptions<T> = {
/**
* Test the equality of two values. If the comparator returns false,
* then the subscribers will be notified with the changed value. If
* the compartor returns true, then the value is considered equal,
* and subscribers are not notified about any change.
*
* This option controls whether or not subscribers get notified on
* changes. Do not directly modify newVal or oldVal, use the transform
* function option for that.
*
* Default comparator is : (newVal, oldVal) => newVal === oldVal
*
* @param newVal
* @param oldVal
*/
comparator: (newVal: T, oldVal: T) => bool
/**
* Intercept the value as it's being set. This function is exected after
* the comparator, therefore newVal equals the value tested against oldVal.
*
* By default, the value is always updated, regardless if the comaprator
* considers them equal or not. To change this behavior, return oldVal if changed
* is false so that the old value is preserved.
*
* This option controls what ReactVar.value returns, if the comparator function
* returns true, subscribers will still be notified regardless whether newVal or oldVal
* is returned. See the comparator function optionn.
*
* Whatever is returned by this function will be returned by ReactVar.value.
*
* Default tranform is : newVal => newVal
*
* @param newVal
* @param oldVal
* @param changed the comparator result
*/
transform: (newVal: T, oldVal: T, changed: bool) => Promise<T> | T
}
/**
* Create a new reactive variable, then return it
*/
const createReactVar<T>(initialValue: T, options: ReactVarOptions<T>): ReactVar<T>;
/**
* React hook that will subscribe to the specified reactive variable
*/
const useReactVar<T>(reactVar: ReactVar<T>): T;
Contribution
All contributions welcome! Every PR must be accompanied by their associated unit tests!
License
MIT