Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor and improve lifecycle management #3

Merged
merged 10 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
"plugin:prettier/recommended",
"next/core-web-vitals"
],
"plugins": ["prettier"]
"plugins": ["@typescript-eslint", "prettier"],
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error"
}
}
14 changes: 3 additions & 11 deletions dist/AtomDevTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.AtomDevTools = void 0;
var react_1 = __importStar(require("react"));
var constants_1 = require("./constants");
var utils_1 = require("./utils");
function AtomObserver(_a) {
var onChange = _a.onChange, _b = _a.onLifeCycle, onLifeCycle = _b === void 0 ? constants_1.noop : _b;
var rootDb = (0, react_1.useContext)(constants_1.RootContext);
var rootDb = (0, utils_1.useDb)();
(0, react_1.useEffect)(function () {
var onLifeCycleWrapper = function (data) {
var refKeys = Array.from(rootDb.activeRefKeys.values());
var activeHooks = Object.fromEntries(refKeys.map(function (key) { return [
key,
rootDb.subscriptions.listenerCount(key)
]; }));
onLifeCycle(__assign(__assign({}, data), { activeHooks: activeHooks }));
onLifeCycle(data);
};
var subscriptions = [
rootDb.subscriptions.on(constants_1.$$internal, onChange),
rootDb.subscriptions.on(constants_1.$$lifeCycleChannel, onLifeCycleWrapper)
];
rootDb.subscriptions.emit(constants_1.$$lifeCycleChannel, {
type: constants_1.LIFECYCLE_MOUNT,
key: constants_1.$$lifeCycleChannel
});
return function () {
subscriptions.forEach(function (unsubscribe) { return unsubscribe(); });
};
Expand Down
5 changes: 2 additions & 3 deletions dist/AtomRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.AtomRoot = void 0;
var react_1 = __importDefault(require("react"));
var db_1 = require("./db");
var react_2 = require("react");
var constants_1 = require("./constants");
var utils_1 = require("./utils");
function AtomRoot(_a) {
var children = _a.children;
var rootDb = (0, react_2.useContext)(constants_1.RootContext);
var isNestedAtomRoot = rootDb !== constants_1.defaultContext;
var currentDb = (0, utils_1.useDb)();
var isNestedAtomRoot = currentDb !== constants_1.defaultContext;
if (isNestedAtomRoot) {
throw new Error((0, utils_1.errorMsg)('Application tree may only be wrapped in a single `AtomRoot` component'));
}
Expand Down
6 changes: 1 addition & 5 deletions dist/core.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import type { AtomRef } from './types';
export type { AtomRef } from './types';
export { useIsNew } from './utils';
export { AtomDevTools } from './AtomDevTools';
export { AtomRoot } from './AtomRoot';
export declare function atomRef<T>({ key, defaultState }: {
key: AtomRef<T>['key'];
defaultState: AtomRef<T>['defaultState'];
}): Readonly<AtomRef<T>>;
export declare function atomRef<T>({ key, defaultState, resetOnInactive }: AtomRef<T>): Readonly<AtomRef<T>>;
export declare function useRead<T, SelectorValue = T>(atomRef: AtomRef<T>, selector: (state: T) => SelectorValue): SelectorValue;
export declare function useSend<T>(atomRef: AtomRef<T>): <Payload>(mutationFn: (oldState: T, payload: Payload) => T, payload: Payload) => Promise<[void, void]>;
export declare function useReset<T>(atomRef: AtomRef<T>): () => Promise<[void, void]>;
37 changes: 15 additions & 22 deletions dist/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ var __assign = (this && this.__assign) || function () {
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useReset = exports.useSend = exports.useRead = exports.atomRef = exports.AtomRoot = exports.AtomDevTools = exports.useIsNew = void 0;
exports.useReset = exports.useSend = exports.useRead = exports.atomRef = exports.AtomRoot = exports.AtomDevTools = void 0;
var react_1 = require("react");
var constants_1 = require("./constants");
var db_1 = require("./db");
var lifecycle_1 = require("./lifecycle");
var mutable_1 = require("./mutable");
var utils_1 = require("./utils");
function defaultTo(defaultValue, value) {
return value === undefined ? defaultValue : value;
}
Expand All @@ -36,69 +36,62 @@ function checkDuplicateAtomKey(key) {
}
return key;
}
var utils_1 = require("./utils");
Object.defineProperty(exports, "useIsNew", { enumerable: true, get: function () { return utils_1.useIsNew; } });
var AtomDevTools_1 = require("./AtomDevTools");
Object.defineProperty(exports, "AtomDevTools", { enumerable: true, get: function () { return AtomDevTools_1.AtomDevTools; } });
var AtomRoot_1 = require("./AtomRoot");
Object.defineProperty(exports, "AtomRoot", { enumerable: true, get: function () { return AtomRoot_1.AtomRoot; } });
function atomRef(_a) {
var key = _a.key, defaultState = _a.defaultState;
var key = _a.key, defaultState = _a.defaultState, _b = _a.resetOnInactive, resetOnInactive = _b === void 0 ? true : _b;
var actualKey = checkDuplicateAtomKey(key);
var ref = {
key: actualKey,
defaultState: defaultState
defaultState: defaultState,
resetOnInactive: resetOnInactive
};
mutable_1.mutable.atomRefsByKey.set(actualKey, ref);
return ref;
}
exports.atomRef = atomRef;
function useRead(atomRef, selector) {
var key = atomRef.key, defaultState = atomRef.defaultState;
var rootDb = (0, react_1.useContext)(constants_1.RootContext);
var rootDb = (0, utils_1.useDb)();
var initialStateSlice = (0, db_1.getState)(rootDb)[key];
var _a = (0, react_1.useState)(selector(defaultTo(defaultState, initialStateSlice))), hookState = _a[0], setHookState = _a[1];
(0, react_1.useEffect)(function () {
var watcherFn = function (_a) {
var newState = _a.newState;
var oldState = _a.oldState, newState = _a.newState;
var prev = oldState[key];
var stateSlice = newState[key];
var nextValue = selector(defaultTo(defaultState, stateSlice));
var hasChanged = hookState !== nextValue;
var hasChanged = prev !== nextValue;
if (!hasChanged) {
return;
}
setHookState(nextValue);
};
return rootDb.subscriptions.on(key, watcherFn);
}, [
rootDb,
key,
hookState,
selector,
defaultState,
atomRef
]);
(0, lifecycle_1.useLifeCycle)(rootDb, atomRef);
}, [rootDb, key, selector, defaultState, atomRef]);
(0, lifecycle_1.useLifeCycle)(atomRef, 'read');
return hookState;
}
exports.useRead = useRead;
function useSend(atomRef) {
var key = atomRef.key, defaultState = atomRef.defaultState;
var rootDb = (0, react_1.useContext)(constants_1.RootContext);
(0, lifecycle_1.useLifeCycle)(rootDb, atomRef);
var rootDb = (0, utils_1.useDb)();
(0, lifecycle_1.useLifeCycle)(atomRef, 'send');
return (0, react_1.useMemo)(function () {
return function (mutationFn, payload) {
var _a;
if (process.env.NODE_ENV === 'development' &&
!mutationFn.name) {
console.error('Warning: This mutation function should be named -', mutationFn);
}
var key = atomRef.key, defaultState = atomRef.defaultState;
var rootState = (0, db_1.getState)(rootDb);
var stateSlice = defaultTo(defaultState, rootState[key]);
var nextState = __assign(__assign({}, rootState), (_a = {}, _a[key] = mutationFn(stateSlice, payload), _a));
return (0, db_1.setState)(rootDb, nextState, atomRef, mutationFn, payload);
};
}, [defaultState, rootDb, key, atomRef]);
}, [rootDb, atomRef]);
}
exports.useSend = useSend;
function useReset(atomRef) {
Expand Down
2 changes: 1 addition & 1 deletion dist/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function makeDb(initialState) {
return {
state: initialState,
subscriptions: subscriptions,
activeRefKeys: new Set()
activeHooks: {}
};
}
exports.makeDb = makeDb;
Expand Down
1 change: 1 addition & 0 deletions dist/extras/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './use-cache';
17 changes: 17 additions & 0 deletions dist/extras/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict";
/*
* Extras are modules exported for external usage as
* additional utilities that are separate from core.
* */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./use-cache"), exports);
8 changes: 8 additions & 0 deletions dist/extras/use-cache.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Returns a new function that compares the old return value
* and new return value. If they are the same, then it will
* return what was previously returned. This is useful for
* determining if two different objects are equal to prevent
* unecessary rerenders.
*/
export declare function useIsNew<X, Y>(fn: (x: X) => Y, isNewValue?: (prev: Y, next: Y) => boolean): (x: X) => Y;
44 changes: 44 additions & 0 deletions dist/extras/use-cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useIsNew = void 0;
var react_1 = require("react");
function shallowCompare(cache, value) {
var maybeNewValue = value !== cache;
if (maybeNewValue) {
var shouldShallowCompare = typeof cache === 'object';
if (shouldShallowCompare) {
for (var _i = 0, _a = Object.keys(value); _i < _a.length; _i++) {
var key = _a[_i];
var prev = cache[key];
var next = value[key];
var isNewValue = prev !== next;
if (isNewValue) {
return true;
}
}
return false;
}
}
return cache !== value;
}
/**
* Returns a new function that compares the old return value
* and new return value. If they are the same, then it will
* return what was previously returned. This is useful for
* determining if two different objects are equal to prevent
* unecessary rerenders.
*/
function useIsNew(fn, isNewValue) {
if (isNewValue === void 0) { isNewValue = shallowCompare; }
var cache = (0, react_1.useRef)(null);
return function (x) {
var next = fn(x);
var shouldUpdateCache = cache.current === null ||
isNewValue(cache.current, next);
if (shouldUpdateCache) {
cache.current = next;
}
return cache.current;
};
}
exports.useIsNew = useIsNew;
1 change: 1 addition & 0 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './core';
export * from './extras';
export * as examples from './examples';
1 change: 1 addition & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.examples = void 0;
__exportStar(require("./core"), exports);
__exportStar(require("./extras"), exports);
exports.examples = __importStar(require("./examples"));
9 changes: 7 additions & 2 deletions dist/lifecycle.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
import type { AtomRef, Db } from './types';
export declare function useLifeCycle(db: Db<any>, atomRef: AtomRef<any>): void;
import type { AtomRef, Db, LifeCycleEventData } from './types';
export declare function useLifeCycle(atomRef: AtomRef<any>, hookType: keyof Db<any>['activeHooks']): void;
export declare function useOnLifeCycle<T>(atomRef: AtomRef<T>, fn: (data: {
type: string;
activeHooks: Db<T>['activeHooks'];
state: Db<T>['state'];
}) => void, predicate?: (data: LifeCycleEventData, atomRef: AtomRef<T>) => boolean): import("emittery").UnsubscribeFn;
Loading