UNPKG

fluidstate-react

Version:

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

96 lines (93 loc) 3.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.withReactive = void 0; var _react = require("react"); var _onceRef = require("./once-ref"); var _fluidstate = require("fluidstate"); let reactiveObjectId = 1; const COMPUTED_CONFIG = { equals: () => false }; /** * A Higher-Order Component (HOC) that makes a React component reactive. * The wrapped component will automatically re-render whenever any `fluidstate` * reactive properties accessed during its render cycle change. * * @template P The props type of the component to wrap. * @param {FunctionComponent<P>} Component The React functional component to make reactive. * @returns {FunctionComponent<P>} A new reactive component that wraps the original one. */ const withReactive = Component => { const FluidComponent = (...args) => { const argsRef = (0, _react.useRef)(args); argsRef.current = args; const data = (0, _onceRef.useOnceRef)(() => ({ isInitial: true, isWithinRender: false, shouldRerender: false, hasError: false, error: null })); const [, rerender] = (0, _react.useState)(Symbol()); const renderControl = (0, _onceRef.useOnceRef)(() => (0, _fluidstate.createAtom)(`withReactive(${Component.displayName || Component.name || "Unknown"})/renderControl@${reactiveObjectId}`)); // We should only rerun component render within rendering context // and in a way that preserves hook order const computed = (0, _onceRef.useOnceRef)(() => (0, _fluidstate.createComputedAtom)(`withReactive(${Component.displayName || Component.name || "Unknown"})/computed@${reactiveObjectId}`, () => { renderControl.current.reportObserved(); if (data.current.isWithinRender) { try { data.current.hasError = false; data.current.error = null; return Component(...argsRef.current); } catch (e) { // Note: throwing inside computed causes issues with // some reactive layers (e.g. in preact layer // it causes out-of-order effect) data.current.hasError = true; data.current.error = e; } } else { data.current.shouldRerender = true; } return null; }, COMPUTED_CONFIG)); const reaction = (0, _onceRef.useOnceRef)(() => (0, _fluidstate.createReaction)(() => { computed.current.get(); if (data.current.isInitial) { return; } if (data.current.shouldRerender) { data.current.shouldRerender = false; (0, _fluidstate.untrack)(() => { rerender(Symbol()); }); } })); (0, _react.useEffect)(() => { return () => { reaction.current.stop(); }; }, []); data.current.isWithinRender = true; renderControl.current.reportChanged(); try { const result = computed.current.get(); if (data.current.hasError) { throw data.current.error; } return result; } finally { data.current.isInitial = false; data.current.shouldRerender = false; data.current.isWithinRender = false; } }; if (Component.displayName || Component.name) { FluidComponent.displayName = `FluidComponent(${Component.displayName || Component.name})`; } return FluidComponent; }; exports.withReactive = withReactive; //# sourceMappingURL=hoc.js.map