fluidstate-react
Version:
Library for using fine-grained reactivity state management library fluidstate in React
89 lines (82 loc) • 3.27 kB
JavaScript
;
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