UNPKG

fluidstate-react

Version:

Library for using fine-grained reactivity state management library fluidstate in React

89 lines (82 loc) 3.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useReactive = void 0; var _react = require("react"); var _onceRef = require("./once-ref"); var _fluidstate = require("fluidstate"); let reactiveObjectId = 1; /** * A React hook that runs a function to derive state from reactive sources * and automatically re-renders the component when any of the accessed * reactive properties change. * * @template T The type of the derived state. * @param {() => T} getState A function that accesses reactive state and returns a value. * This function is re-executed whenever its reactive dependencies change. * @param {ReadonlyArray<unknown>} [dependencyArray=[]] An optional array of dependencies, * similar to `useMemo` or `useEffect`. The `getState` function will be re-created * if any of these dependencies change. * @returns {T} The result of the `getState` function. */ const useReactive = (getState, dependencyArray = []) => { const data = (0, _onceRef.useOnceRef)(() => ({ isInitial: true, isWithinRender: false, shouldRerender: false })); data.current.isWithinRender = true; // result is a holder for the value returned from getState const [, rerender] = (0, _react.useState)(Symbol()); // Since we put the get state under dependency array, it'll change when // dependency array is changed const getStateCallback = (0, _react.useCallback)(getState, dependencyArray); // A reactive atom for getState function which will be used in the effect const getStateDetails = (0, _onceRef.useOnceRef)(() => ({ atom: (0, _fluidstate.createAtom)(`useReactive/getStateDetails/atom@${reactiveObjectId++}`), callback: getStateCallback })); // Upon dependency array change, if the callback is different, it'll recalculate // the effect since it depends on it if (getStateDetails.current.callback !== getStateCallback) { getStateDetails.current.callback = getStateCallback; getStateDetails.current.atom.reportChanged(); } const computed = (0, _onceRef.useOnceRef)(() => (0, _fluidstate.createComputedAtom)(`useReactive/computed@${reactiveObjectId++}`, () => { getStateDetails.current.atom.reportObserved(); if (!data.current.isWithinRender) { data.current.shouldRerender = true; } return getStateDetails.current.callback(); })); const reaction = (0, _onceRef.useOnceRef)(() => (0, _fluidstate.createReaction)(() => { computed.current.get(); // If the result is first initialized or happens due to dependency array change // during render, we do not need to rerender, but is rerendered upon other changes if (data.current.isInitial) { return; } if (data.current.shouldRerender) { data.current.shouldRerender = false; (0, _fluidstate.untrack)(() => { rerender(Symbol()); }); } })); // The effect is stopped when component unmounts (0, _react.useEffect)(() => { return () => { reaction.current.stop(); }; }, []); try { return computed.current.get(); } finally { data.current.isInitial = false; data.current.isWithinRender = false; data.current.shouldRerender = false; } }; exports.useReactive = useReactive; //# sourceMappingURL=hooks.js.map