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