@diginet/use-reactive
Version:
A reactive state management hook for React.
765 lines (760 loc) • 35.4 kB
JavaScript
import React, { useRef, useState, useEffect, createContext, useContext } from 'react';
// Utility function to compare two objects
function isEqual(x, y) {
const ok = Object.keys, tx = typeof x, ty = typeof y;
return x && y && tx === 'object' && tx === ty ? (ok(x).length === ok(y).length &&
ok(x).every(key => isEqual(x[key], y[key]))) : (x === y);
}
/**
* Recursively synchronizes and updates a structure containing information about each property.
* - Tracks state changes and updates properties accordingly.
* - Supports nested objects, while avoiding functions and getters.
* - Uses a WeakMap for tracking object property maps.
*
* @param obj - The target object whose properties are being tracked.
* @param stateMapRef - A WeakMap that associates objects with their corresponding property maps.
* @param stateMap - A Map storing metadata about properties, including modification status and last known value.
* @param newObj - An optional new object to compare against and apply updates from.
*/
const syncState = (obj, stateMapRef, stateMap, newObj) => {
for (const key of Object.keys(obj)) {
let descriptor = Object.getOwnPropertyDescriptor(obj, key);
if (newObj) {
const newDescriptor = Object.getOwnPropertyDescriptor(newObj, key);
if (newDescriptor) {
descriptor = newDescriptor;
}
}
// Determine if the property is a function or getter
const isFunction = descriptor?.value && typeof descriptor.value === "function";
const isGetter = descriptor?.get && typeof descriptor.get === "function";
if (isFunction || isGetter) {
// Re-define getters and functions without modifications
Object.defineProperty(obj, key, descriptor);
continue;
}
const value = obj[key];
// Handle primitive values and arrays
if (typeof value !== "object" || Array.isArray(value)) {
if (typeof value === "function")
continue; // Redundant check for safety
// Retrieve stored property metadata
const [modifiedFlag, , lastPropValue] = stateMap.get(key) || [undefined, undefined, undefined];
const newValue = newObj ? newObj[key] : value;
const propValueChanged = !isEqual(lastPropValue, newValue);
// Update the object property if it has changed
if (newObj && propValueChanged && obj[key] !== newValue) {
obj[key] = newValue;
}
// Update state tracking map
stateMap.set(key, [modifiedFlag ?? false, undefined, newValue]);
}
else {
// Handle nested objects
let childStateMap = stateMap.get(key)?.[1];
if (!childStateMap) {
childStateMap = new Map();
stateMap.set(key, [false, childStateMap, value]);
}
// Track nested object in the WeakMap
stateMapRef.set(value, childStateMap);
// Recursively sync nested properties
syncState(value, stateMapRef, childStateMap, newObj ? newObj[key] : undefined);
}
}
};
/**
* useReactive - A custom React hook that creates a reactive state object.
*
* This hook provides a proxy-based reactive state that triggers re-renders
* when properties change. It also supports computed properties (getters),
* hot module reloading (HMR) synchronization, and optional side effects.
*
* @param inputState - The initial state object.
* @param effect - Optional effect function that runs when dependencies change.
* @param deps - Dependencies array for triggering reactivity updates.
* @returns A reactive proxy of the state object.
*/
function useReactive(inputState, options) {
if (typeof window === "undefined") {
throw new Error("useReactive should only be used in the browser");
}
const reactiveStateRef = useRef(inputState);
const [, setTriggerF] = useState(0); // State updater to trigger re-renders
const setTrigger = (foo) => {
if (!options?.noUseState) {
setTriggerF(foo);
}
};
const proxyRef = useRef(null);
const nestedproxyrefs = useRef(new Map());
const stateMapRef = useRef(new WeakMap());
const subscribersRef = useRef([]);
const historyRef = useRef([]);
const historySettingsRef = useRef({ enabled: false, maxDepth: 100 });
const redoStack = useRef([]);
if (options?.historySettings?.enabled)
historySettingsRef.current.enabled = options.historySettings.enabled;
if (options?.historySettings?.maxDepth)
historySettingsRef.current.maxDepth = options.historySettings.maxDepth;
let stateMap = stateMapRef.current.get(reactiveStateRef.current);
if (!stateMap) {
stateMap = new Map();
stateMapRef.current.set(reactiveStateRef.current, stateMap);
}
// Sync the current state with the input object
syncState(reactiveStateRef.current, stateMapRef.current, stateMap, inputState);
// Subscribe to the state object and track changes
function subscribe(targets, callback, recursive, onRead) {
let result = () => { };
if (subscribersRef.current && targets) {
// Create a new subscriber object, start recording target accesses
const subscriber = {
callback,
recording: true,
onRead,
targets: []
};
// Add the subscriber to the list
subscribersRef.current?.push(subscriber);
// Get the target object
const target = targets();
// Handle nested targets
if (recursive === 'deep' || recursive === true) {
// Iterate over all properties of the target object except functions and getters, also possibly handle nested objects
function iterate(target) {
for (const key of Object.keys(target)) {
const descriptor = Object.getOwnPropertyDescriptor(target, key);
const isFunction = descriptor?.value && typeof descriptor.value === "function";
const isGetter = descriptor?.get && typeof descriptor.get === "function";
if (isFunction || isGetter) {
continue;
}
const value = target[key];
if (recursive === 'deep' && typeof value === "object" && !Array.isArray(value)) {
iterate(value);
}
}
}
if (target && typeof target === 'object' && !Array.isArray(target)) {
iterate(target);
}
if (target && typeof target === 'object' && Array.isArray(target)) {
for (const value of target) {
if (typeof value === 'object' && !Array.isArray(value)) {
iterate(value);
}
}
}
}
// Stop recording target accesses
subscriber.recording = false;
result = () => {
const index = subscribersRef.current?.indexOf(subscriber);
if (index !== undefined && index !== -1) {
subscribersRef.current?.splice(index, 1);
}
};
}
return result;
}
// History functions
// Enable/disable history
const enableHistory = (enabled, maxDepth) => {
if (enabled !== undefined) {
historySettingsRef.current.enabled = enabled;
if (!enabled) {
historyRef.current = [];
redoStack.current = [];
}
}
if (maxDepth !== undefined)
historySettingsRef.current.maxDepth = maxDepth;
return historySettingsRef.current;
};
// Undo/redo functions
// Undo the last change
const undoLast = () => {
if (historyRef.current.length === 0)
return;
const lastChange = historyRef.current.pop();
if (lastChange !== undefined) {
if (redoStack.current.length >= (historySettingsRef.current.maxDepth ?? 100))
redoStack.current.shift();
redoStack.current.push({ id: lastChange.id, obj: lastChange.obj, key: lastChange.key, previous: lastChange.previous, value: lastChange.value, timestamp: lastChange.timestamp });
const savedHistoryEnabled = historySettingsRef.current.enabled;
historySettingsRef.current.enabled = false;
lastChange.obj[lastChange.key] = lastChange.previous;
historySettingsRef.current.enabled = savedHistoryEnabled;
}
};
// Undo the last change or a specific change
const undo = (index) => {
if (index !== undefined && (index < 0 || index >= historyRef.current.length))
return;
if (index !== undefined) {
while (historyRef.current.length > index) {
undoLast();
}
}
else {
undoLast();
}
};
// Redo the last undone change (or all undone changes)
const redo = (all) => {
if (redoStack.current.length === 0)
return;
do {
const redoChange = redoStack.current.pop();
if (redoChange) {
redoChange.obj[redoChange.key] = redoChange.value;
}
} while (all && redoStack.current.length > 0);
};
// Revert to a specific change
const revert = (index) => {
if (index < 0 || index >= historyRef.current.length)
return;
const entry = historyRef.current[index];
if (entry) {
const savedHistoryEnabled = historySettingsRef.current.enabled;
historySettingsRef.current.enabled = false;
entry.obj[entry.key] = entry.previous;
historySettingsRef.current.enabled = savedHistoryEnabled;
historyRef.current.splice(index, 1);
}
};
// Clear the history
const clear = () => {
historyRef.current = [];
redoStack.current = [];
setTrigger((prev) => prev + 1);
};
// Get the current snapshot
const snapshot = () => {
return historyRef.current.length > 0 ? historyRef.current[historyRef.current.length - 1].id : null;
};
// Restore a specific snapshot
const restore = (id) => {
let index;
if (id === null) {
index = 0;
}
else {
index = historyRef.current.findIndex(entry => (entry.id === id));
}
if (index < 0)
return;
redoStack.current = [];
if (id !== null) {
while (historyRef.current.length > index + 1) {
undo();
}
}
else {
while (historyRef.current.length > 0) {
undo();
}
}
};
// History object
const history = { enable: enableHistory, clear, undo, redo, revert, snapshot, restore, entries: historyRef.current };
// Create a proxy for the state object if it doesn't exist
if (!proxyRef.current) {
const createReactiveProxy = (target) => {
const proxy = new Proxy(target, {
get(obj, prop) {
const key = prop;
if (subscribersRef.current) {
for (const subscriber of subscribersRef.current) {
if (subscriber.recording) {
const exists = subscriber.targets.some(target => target.obj === obj && target.prop === key);
if (!exists) {
subscriber.targets.push({ obj, prop: key });
}
}
}
}
let value;
// Handle computed properties (getters)
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
const isFunction = descriptor?.value && typeof descriptor.value === "function";
const isGetter = descriptor?.get && typeof descriptor.get === "function";
if (isGetter) {
value = descriptor.get.call(proxy);
}
else
value = obj[key];
// Proxy arrays to trigger updates on mutating methods
if (Array.isArray(value)) {
return new Proxy(value, {
get(arrTarget, arrProp) {
const prevValue = [...arrTarget];
const arrValue = arrTarget[arrProp];
// If accessing a possibly mutating array method, return a wrapped function
if (typeof arrValue === "function") {
return (...args) => {
const result = arrValue.apply(arrTarget, args);
if (!isEqual(prevValue, arrTarget)) {
const stateMap = stateMapRef.current?.get(obj);
if (!stateMap?.has(prop))
return false;
const [, map, propValue] = stateMap.get(prop);
if (!isEqual(prevValue, arrTarget)) {
stateMap.set(prop, [true, map, propValue]);
obj[prop] = arrTarget;
setTrigger((prev) => prev + 1);
}
}
return result;
};
}
return arrValue;
},
});
}
// Wrap nested objects in proxies
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
let result = nestedproxyrefs.current.get(value);
if (!result) {
result = createReactiveProxy(value);
nestedproxyrefs.current.set(value, result);
}
return result;
}
// Ensure functions are bound to the proxy object
if (isFunction) {
return function (...args) {
return value.apply(proxy, args); // Now correctly bound to the current proxy
};
}
if (subscribersRef.current) {
for (const subscriber of subscribersRef.current) {
if (subscriber.onRead && !subscriber.recording) {
if (subscriber.targets.some(target => target.obj === obj && target.prop === prop)) {
subscriber.callback.call(proxy, proxy, prop, value, value, true);
}
}
}
}
return value;
},
set(obj, prop, value) {
const stateMap = stateMapRef.current?.get(obj);
if (!stateMap?.has(prop))
return false;
const [, map, propValue] = stateMap.get(prop);
const previousValue = obj[prop];
if (!isEqual(previousValue, value)) {
stateMap.set(prop, [true, map, propValue]);
obj[prop] = value;
if (historySettingsRef.current.enabled) {
if (historyRef.current.length >= (historySettingsRef.current.maxDepth ?? 100))
historyRef.current.shift();
historyRef.current.push({ id: crypto.randomUUID(), obj: proxy, key: prop, previous: previousValue, value, timestamp: Date.now() });
}
setTrigger((prev) => prev + 1);
}
if (subscribersRef.current) {
for (const subscriber of subscribersRef.current) {
if (!subscriber.recording) {
if (subscriber.targets.some(target => target.obj === obj && target.prop === prop)) {
subscriber.callback.call(proxy, proxy, prop, value, previousValue, false);
}
}
}
}
return true;
},
});
return proxy;
};
proxyRef.current = createReactiveProxy(reactiveStateRef.current);
// If we have an init method, call it after creation
if (options?.init && typeof options?.init === "function") {
options.init.call(proxyRef.current, proxyRef.current, subscribe, history);
}
}
// useEffect to handle side effects and cleanup
if (options?.effects) {
function getNestedValue(obj, path, defaultValue) {
if (!obj || typeof obj !== 'object' || !path)
return defaultValue;
// Convert bracket notation to dot notation (e.g., "user.address[0].city" -> "user.address.0.city")
const normalizedPath = path.replace(/\[(\d+)\]/g, '.$1');
return normalizedPath.split('.').reduce((acc, key) => {
if (acc === null || acc === undefined)
return undefined;
return acc[key];
}, obj) ?? defaultValue;
}
function getDeps(deps) {
if (!deps)
return undefined;
return deps.map(prop => (typeof prop === 'symbol') ? getNestedValue(reactiveStateRef.current, prop.description) : prop);
}
// Multiple effect/dependency pairs
for (const [effectF, effectDeps] of options.effects) {
useEffect(() => {
let cleanup;
if (effectF && proxyRef.current) {
cleanup = effectF.apply(proxyRef.current, [proxyRef.current, subscribe, history]);
}
return () => {
if (cleanup)
cleanup();
};
}, effectDeps ? getDeps(effectDeps.call(reactiveStateRef.current, reactiveStateRef.current, subscribe, history)) : []);
}
}
// Return the reactive proxy object etc
return [proxyRef.current, subscribe, history];
}
/**
* Creates a globally shared reactive state using React Context and useReactive.
*
* @param initialState The initial state object
* @returns {ReactiveStoreProvider, useReactiveStore} Context provider and hook
*/
function createReactiveStore(initialState, options) {
// Create the context and provider
const ReactiveStoreContext = createContext(null);
const ReactiveStoreProvider = ({ children }) => {
// Create the reactive state
const [state, subscribe, history] = useReactive(initialState, { ...options, noUseState: true });
// Return the provider
return (React.createElement(ReactiveStoreContext.Provider, { value: [state, subscribe, history] }, children));
};
/**
* Hook to access the reactive state and subscribe to changes.
*
* @returns {ReactiveStoreContext<T>} The reactive state and subscribe function
*/
const useReactiveStore = () => {
const context = useContext(ReactiveStoreContext);
if (!context) {
throw new Error("useReactiveStore must be used within a ReactiveStoreProvider");
}
const [, setTrigger] = React.useState(0);
// Create a proxy to track the subscriptions
const proxyProxy = useReactive(context[0], { noUseState: true });
// Track the subscriptions
const subscriptionsRef = useRef(null);
if (!subscriptionsRef.current) {
subscriptionsRef.current = new WeakMap();
}
const removerRef = useRef(null);
if (!removerRef.current) {
// Subscribe to the proxy to track the subscriptions
removerRef.current = proxyProxy[1](() => proxyProxy[0], function (state, key, _value, _previous, read) {
// Get the map of subscriptions for the state
let map = subscriptionsRef.current.get(state);
if (!map) {
// Create a new map if it doesn't exist
map = new Map();
subscriptionsRef.current.set(state, map);
}
// Check if the key is not already subscribed
if (read && !map.has(key)) {
// Subscribe to the key
map.set(key, true);
context[1](() => state[key], () => {
setTrigger((prev) => prev + 1);
});
}
}, 'deep', true);
}
return [proxyProxy[0], context[1], context[2]];
};
return [ReactiveStoreProvider, useReactiveStore];
}
// Example 1: Simple Counter
const Counter = () => {
const [state] = useReactive({ count: 0 });
return (React.createElement("div", null,
React.createElement("h3", null, "Counter"),
React.createElement("p", null,
"Count: ",
state.count),
React.createElement("button", { onClick: () => state.count++ }, "Increment"),
React.createElement("button", { onClick: () => state.count-- }, "Decrement")));
};
// Example 2: Computed Property
const ComputedPropertyExample = () => {
const [state] = useReactive({
count: 2,
get double() {
return this.count * 2;
},
});
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Computed Property"),
React.createElement("p", null,
"Count: ",
state.count),
React.createElement("p", null,
"Double: ",
state.double),
React.createElement("button", { onClick: () => state.count++ }, "Increment")));
};
// Example 3: Async State Update
const AsyncExample = () => {
const [state] = useReactive({
count: 0,
async incrementAsync() {
await new Promise((resolve) => setTimeout(resolve, 1000));
this.count++;
},
});
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Async Update"),
React.createElement("p", null,
"Count: ",
state.count),
React.createElement("button", { onClick: () => state.incrementAsync() }, "Increment Async")));
};
// Example 4: Nested State
const NestedStateExample = () => {
const [state] = useReactive({
nested: { value: 10 },
});
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Nested State"),
React.createElement("p", null,
"Nested Value: ",
state.nested.value),
React.createElement("button", { onClick: () => state.nested.value++ }, "Increment Nested")));
};
// Example 5: Single Effect
const SingleEffectExample = () => {
const [state] = useReactive({ count: 0 }, {
effects: [[
function () {
console.log("Count changed:", this.count);
},
function () {
return [this.count];
}
]]
});
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Single Effect"),
React.createElement("p", null,
"Count: ",
state.count),
React.createElement("button", { onClick: () => state.count++ }, "Increment")));
};
// Example 6: Multiple Effects
const MultipleEffectsExample = () => {
const [state] = useReactive({ count: 0, text: "Hello" }, {
effects: [
[function () { console.log("Count changed:", this.count); }, () => []],
[function () { console.log("Text changed:", this.text); }, () => []],
]
});
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Multiple Effects"),
React.createElement("p", null,
"Count: ",
state.count),
React.createElement("p", null,
"Text: ",
state.text),
React.createElement("button", { onClick: () => state.count++ }, "Increment Count"),
React.createElement("button", { onClick: () => (state.text = "World") }, "Change Text")));
};
const ControlButtons = ({ onIncrement, onDecrement, incrementLabel, decrementLabel }) => (React.createElement("div", null,
React.createElement("button", { onClick: onIncrement }, incrementLabel),
onDecrement && React.createElement("button", { onClick: onDecrement }, decrementLabel)));
const ReactiveChild = ({ initialCount }) => {
const [state] = useReactive({ count: initialCount }, {
init() {
console.log("Count changed due to prop update:", this.count);
},
});
return (React.createElement("div", null,
React.createElement("h3", null, "Reactive Child"),
React.createElement("p", null,
"Count: ",
state.count),
React.createElement(ControlButtons, { onIncrement: () => state.count++, incrementLabel: "Increment" })));
};
// Example using ReactiveChild to test prop dependency
const EffectDependencyExample = () => {
const [state] = useReactive({ count: 0 });
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Effect Dependency Example"),
React.createElement("p", null,
"Parent Count: ",
state.count),
React.createElement(ControlButtons, { onIncrement: () => state.count++, incrementLabel: "Increment Parent" }),
React.createElement(ReactiveChild, { initialCount: state.count })));
};
// Example using ReactiveChild to test prop dependency
const ArrayExample = () => {
const [state] = useReactive({
todos: ['hello'],
addWorld() {
this.todos = [...this.todos, ' world'];
},
addExclamation() {
this.todos.push('!');
}
});
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Array Example"),
React.createElement("p", null,
"todos: ",
state.todos.map(todo => todo)),
React.createElement("button", { onClick: state.addWorld }, "Add world"),
React.createElement("button", { onClick: state.addExclamation }, "Add !")));
};
const [ReactiveStoreProvider, useReactiveStore] = createReactiveStore({
counter: 0,
user: { name: "John Doe", age: 30 },
}, { init(_state, _subscribe, history) { history.enable(true); } });
function ReactiveStoreUser() {
const [store, _subscribe, history] = useReactiveStore();
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Reactive store user"),
React.createElement("p", null,
"Name: ",
store.user.name,
", Age: ",
store.user.age),
React.createElement("button", { onClick: () => store.user.name = "Jane Doe" }, "Change name"),
React.createElement("button", { onClick: () => store.user.age++ }, "Increment age"),
React.createElement("button", { onClick: () => history.undo() }, "Undo")));
}
function AnotherReactiveStoreUser() {
const [store, _subscribe, history] = useReactiveStore();
return (React.createElement("div", null,
React.createElement("h3", null, "Another Reactive store user"),
React.createElement("p", null,
"Name: ",
store.user.name,
", Age: ",
store.user.age),
React.createElement("button", { onClick: () => store.user.name = "Jane Doe" }, "Change name"),
React.createElement("button", { onClick: () => store.user.age++ }, "Increment age"),
React.createElement("button", { onClick: () => history.redo() }, "Redo")));
}
const SubscribedCounter = () => {
const [items, setItems] = useState([]);
const [state] = useReactive({
count: 0,
count2: 0,
}, {
init(_state, subscribe) {
this.count = 10;
setItems(items => [...items, "SubscribedCounter initialized"]);
subscribe(() => [this.count2, this.count], (_state, key, value, previous) => {
setItems(items => [...items, `${key} changed from ${previous} to ${value}`]);
});
}
});
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "Subscribed Counter"),
React.createElement("p", null,
"Count: ",
state.count,
" Count2: ",
state.count2),
React.createElement("button", { onClick: () => state.count++ }, "Increment"),
React.createElement("button", { onClick: () => state.count-- }, "Decrement"),
React.createElement("button", { onClick: () => state.count2++ }, "Increment 2"),
React.createElement("button", { onClick: () => state.count2-- }, "Decrement 2"),
React.createElement("p", null,
"Items: ",
React.createElement("button", { onClick: () => setItems([]) }, "Clear")),
items.map((item, index) => (React.createElement("p", { key: index }, item)))));
};
const ExampleComponent = () => {
const [state, , history] = useReactive({ count: 0 }, { historySettings: { enabled: true } });
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null,
"Count: ",
state.count),
React.createElement("button", { onClick: () => state.count++ }, "Increment"),
React.createElement("button", { onClick: () => history.undo() }, "Undo"),
React.createElement("button", { onClick: () => history.redo() }, "Redo")));
};
const CheckBox = ({ caption, checked, setChecked }) => {
return (React.createElement("div", null,
React.createElement("label", null,
React.createElement("input", { type: "checkbox", checked: checked, onChange: (e) => setChecked(e.target.checked) }),
caption)));
};
const ReactiveHistoryExample = () => {
const [state, , history] = useReactive({ checked: false, sub: { text1: "", text2: "" } });
const [historyEnabled, setHistoryEnabled] = useState(false);
const [snapshot, setSnapshot] = useState(undefined);
return (React.createElement("div", null,
"_________________________________",
React.createElement("h3", null, "History"),
React.createElement(CheckBox, { caption: "Some boolean", checked: state.checked, setChecked: (checked) => state.checked = checked }),
React.createElement("input", { type: "text", value: state.sub.text1, onChange: (e) => (state.sub.text1 = e.target.value) }),
React.createElement("p", null),
React.createElement("input", { type: "text", value: state.sub.text2, onChange: (e) => (state.sub.text2 = e.target.value) }),
React.createElement("br", null),
React.createElement(CheckBox, { caption: "Enable history", checked: historyEnabled, setChecked: (checked) => {
setHistoryEnabled(checked);
history.enable(checked);
} }),
React.createElement("br", null),
React.createElement("button", { onClick: () => history.undo(), disabled: !historyEnabled }, "Undo"),
React.createElement("button", { onClick: () => history.undo(0), disabled: !historyEnabled }, "Undo all"),
React.createElement("button", { onClick: () => history.redo(), disabled: !historyEnabled }, "Redo"),
React.createElement("button", { onClick: () => history.redo(true), disabled: !historyEnabled }, "Redo all"),
React.createElement("button", { onClick: () => history.clear(), disabled: !historyEnabled }, "Clear"),
React.createElement("p", null),
React.createElement("button", { onClick: () => setSnapshot(history.snapshot()), disabled: !historyEnabled }, "Take snapshot"),
React.createElement("button", { onClick: () => history.restore(snapshot), disabled: snapshot === undefined || !historyEnabled }, "Restore snapshot"),
React.createElement("h4", null, "Changes:"),
React.createElement("ul", { style: { minHeight: '800px', overflowY: 'scroll' } }, history.entries.map((entry, index) => (React.createElement("div", { key: index, style: { display: 'flex' } },
React.createElement("li", { key: entry.id },
"[",
new Date(entry.timestamp).toLocaleTimeString(),
"]\u00A0",
entry.key,
"\u00A0 \"",
String(entry.previous),
"\" \u2192 \"",
String(entry.value),
"\"\u00A0",
React.createElement("button", { onClick: () => history.revert(index) }, "Revert"),
"\u00A0",
React.createElement("button", { onClick: () => history.undo(index) }, "Undo to here"))))))));
};
// Super Component to Include All Examples
const Examples = () => {
return (React.createElement("div", null,
React.createElement("h2", null, "useReactive Examples"),
React.createElement(Counter, null),
React.createElement(ComputedPropertyExample, null),
React.createElement(AsyncExample, null),
React.createElement(NestedStateExample, null),
React.createElement(SingleEffectExample, null),
React.createElement(EffectDependencyExample, null),
React.createElement(ArrayExample, null),
React.createElement(MultipleEffectsExample, null),
React.createElement(ReactiveStoreProvider, null,
React.createElement(ReactiveStoreUser, null),
React.createElement(AnotherReactiveStoreUser, null)),
React.createElement(SubscribedCounter, null),
React.createElement(ExampleComponent, null),
React.createElement(ReactiveHistoryExample, null)));
};
export { Examples, SubscribedCounter, createReactiveStore, useReactive };
//# sourceMappingURL=index.esm.js.map