react-rv
Version:
react-rv is a lightweight and efficient state management library for React that allows you to create reactive variables and subscribe to them with minimal overhead.
244 lines (239 loc) • 7.52 kB
text/typescript
/**
* A function that listens to reactive variable updates.
*
* @template T The type of the value being listened to.
*
* @param val The updated value of the reactive variable.
* @param old The old value of the reactive variable.
*/
type Listener<T> = (val: T, old: T) => void;
/**
* A function that cleans up event listener.
*/
type CleanupFn = () => void;
/**
* A function to compare old and new values for equality.
*
* @template T The type of values being compared.
*
* @param oldValue The previous value.
* @param newValue The new value to compare against.
*
* @returns `true` if values are considered equal, `false` otherwise.
*/
type EqualFn<T> = (oldValue: T, newValue: T) => boolean;
/**
* Options for configuring a reactive variable.
*
* @template T The type of the reactive variable's value.
*/
type RvOptions<T> = {
/**
* Custom equality function to determine if the value should update.
* If set to `false`, the default equality function will be used.
*/
eq?: false | EqualFn<T>;
};
/**
* A reactive variable (RV) function that allows getting, setting, and subscribing to changes.
*
* @template T The type of the stored value.
*/
interface Rv<T> {
/**
* Retrieves the current value of the reactive variable.
*
* @returns The current value of type `T`.
*/
(): T;
/**
* Updates the value of the reactive variable.
*
* @param newValue The new value to set.
* @param options Optional configuration for the update.
*
* @returns The updated value.
*/
(newValue: T, options?: RvOptions<T>): T;
/**
* Subscribes a listener to value changes.
*
* @param listener A callback function that is triggered when the value changes.
*
* @returns A cleanup function to remove the listener.
*/
on: (listener: Listener<T>) => CleanupFn;
/**
* Unsubscribes a listener from value changes.
*
* @param listener A callback function that is needed to be unsubscribed.
*/
off: (listener: Listener<T>) => void;
/**
* Returns the number of listeners for this reactive variable.
*
* @returns listener count.
*/
size: () => number;
}
/**
* Configuration options for initializing a reactive variable.
*
* @template T The type of the reactive variable's value.
*/
type RvInitOptions<T> = {
/**
* Custom equality function to determine if the value should update.
*/
eq?: EqualFn<T>;
/**
* A callback function triggered whenever the reactive variable is updated.
*
* @param val The new value of the reactive variable.
*/
on?: Listener<T>;
};
/**
* A factory function for creating reactive variables.
*/
interface RvInit {
/**
* Creates a reactive variable (RV), allowing value retrieval, updates, and subscriptions.
*
* @template T The type of the stored value.
*
* @param val The initial value of the reactive variable.
* @param options Optional configuration for the reactive variable.
*
* @returns A reactive variable function that allows getting, setting, and listening for updates.
*/
<T>(val: T, options?: RvInitOptions<T>): Rv<T>;
/**
* Creates a reactive variable from an initializer function.
* The function is immediately executed to determine the initial value.
*
* @template T The type of the stored value.
*
* @param init A function that returns the initial value.
* @param options Optional configuration for equality comparison and event listeners.
*
* @returns A reactive variable function.
*/
fn: <T>(init: () => T, options?: RvInitOptions<T>) => Rv<T>;
}
/**
* Creates a reactive variable (RV), allowing value retrieval, updates, and subscriptions.
*
* @template T The type of the stored value.
*
* @param val The initial value of the reactive variable.
* @param opts Optional configuration for the reactive variable.
*
* @returns A reactive variable function that allows getting, setting, and listening for updates.
*
* @example
* ```ts
* // initialize a state variable with initial value set to 0
* const positiveVar = rv(0, {
* // all options are optional
*
* // define custom `eq` function that will be run on every value set to determine
* // whether or not value is going to be updated
* eq: (oldValue, newValue) => newValue > oldValue && newValue >= 0,
*
* // define callback that's going to be run on every change
* on: (newValue, oldValue) => {}
* })
*
* // alternatively, there's a handy function initializer
* // all options are identical
* const positiveVar = rv.fn(() => 0)
*
* // call variable function with no arguments to get its current value
* const currentValue = positiveVar()
*
* // call variable function passing an argument in order to set it
* // Won't trigger an update because the values are "equal" under this custom rule.
* positiveVar(-3, {
* // override the initial `eq` function for this update call.
* eq: (oldValue, newValue) => newValue > oldValue
* // additionally, you can disable initial `eq` function by passing `false` here
* // it will use a default `eq` function which is just a strict check: `===`
* eq: false // in this case, update WILL happen
* })
*
* // you can also subscribe to value without using any hooks
* const unsubscribe = positiveVar.on((newValue, oldValue) => console.log(newValue, oldValue))
*
* positiveVar(4) // logs: 4
*
* unsubscribe()
*
* positiveVar(5) // there will be no logs
* ```
*/
declare function rv<T>(val: T, opts?: RvInitOptions<T>): Rv<T>;
declare namespace rv {
var fn: <T>(init: () => T, options?: RvInitOptions<T>) => Rv<T>;
}
declare namespace rv {
/**
* Infers the type of the reactive variable.
*
* @example
* ```ts
* const darkMode = rv(false)
*
* type DarkMode = rv.infer<typeof darkMode> // infers as boolean
* ```
*/
type infer<T extends Rv<any>> = T extends Rv<infer X> ? X : never;
}
/**
* Subscribes to a reactive variable and provides its current value,
* updating the state when the variable is updated.
*
* @template T The type of the reactive variable's value.
* @param rv The reactive variable to subscribe to.
*
* @returns The current value of the reactive variable.
*
* @example
* ```tsx
* import React from 'react'
* import { rv, useRv } from 'react-rv'
*
* const counter = rv(0)
*
* const Counter = () => {
* const value = useRv(counter)
* return (
* <div>
* <p>Count: {value}</p>
* <button onClick={() => counter(value + 1)}>Increment</button>
* </div>
* )
* }
* ```
*/
declare const useRv: <T>(rv: Rv<T>) => T;
/**
* React hook to subscribe to updates of a reactive variable.
*
* Note: callback is always using current scope.
*
* @example
* ```ts
* const counter = rv(0)
*
* useRvEffect(counter, (newVal, oldVal) => {
* console.table({ newVal, oldVal })
* })
* ```
*
* @param rv - The reactive variable to subscribe to.
* @param f - A callback function triggered whenever the reactive variable is updated.
* The first and second arguments are the new and the old value of this `rv`.
*/
declare function useRvEffect<T>(rv: Rv<T>, f: Listener<T>): void;
export { type CleanupFn, type EqualFn, type Listener, type Rv, type RvInit, type RvInitOptions, type RvOptions, rv, useRv, useRvEffect };