@zedux/atoms
Version:
A Molecular State Engine for React
76 lines (75 loc) • 2.97 kB
JavaScript
import { createInjector } from '../factories/createInjector.js';
import { haveDepsChanged, prefix } from '../utils/index.js';
const getTask = (effect, descriptor) => {
const task = () => {
const cleanup = effect();
// now that the task has run, there's no need for the scheduler cleanup
// function; replace it with the cleanup logic returned from the effect
// (if any). If a promise was returned, ignore it.
descriptor.cleanup = typeof cleanup === 'function' ? cleanup : undefined;
};
return task;
};
/**
* Runs a deferred side effect. This is just like React's `useEffect`. When
* `deps` change on a subsequent reevaluation, the previous effect will be
* cleaned up and the effect will rerun.
*
* Return a cleanup function to clean up resources when the effect reruns or the
* current atom instance is destroyed.
*
* Unlike `useEffect`, you can return a promise from `injectEffect` (e.g. by
* passing an async function). This is only for convenience in cases where you
* don't have anything to cleanup, as you'll be unable to clean up resources if
* you return a promise.
*/
export const injectEffect = createInjector('injectEffect', (instance, effect, deps, config) => {
const descriptor = {
deps,
type: `${prefix}/effect`,
};
if (!instance.ecosystem.ssr) {
const task = getTask(effect, descriptor);
descriptor.cleanup = () => {
instance.ecosystem._scheduler.unschedule(task);
descriptor.cleanup = undefined;
};
if (config === null || config === void 0 ? void 0 : config.synchronous) {
task();
}
else {
instance.ecosystem._scheduler.schedule({
task,
type: 4, // RunEffect (4)
});
}
}
return descriptor;
}, (prevDescriptor, instance, effect, deps, config) => {
var _a;
if (instance.ecosystem.ssr)
return prevDescriptor;
const depsHaveChanged = haveDepsChanged(prevDescriptor === null || prevDescriptor === void 0 ? void 0 : prevDescriptor.deps, deps);
if (!depsHaveChanged)
return prevDescriptor;
(_a = prevDescriptor.cleanup) === null || _a === void 0 ? void 0 : _a.call(prevDescriptor);
const task = getTask(effect, prevDescriptor);
// this cleanup should be unnecessary since effects run immediately every
// time except init. Leave this though in case we add a way to update an
// atom instance without flushing the scheduler
prevDescriptor.cleanup = () => {
instance.ecosystem._scheduler.unschedule(task);
prevDescriptor.cleanup = undefined;
};
prevDescriptor.deps = deps;
if (config === null || config === void 0 ? void 0 : config.synchronous) {
task();
}
else {
instance.ecosystem._scheduler.schedule({
task,
type: 4, // RunEffect (4)
});
}
return prevDescriptor;
});