From 7b203c557127ca0074a98132cfbfd380f9e48982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ti=E1=BA=BFn=20Nguy=E1=BB=85n=20Kh=E1=BA=AFc?= Date: Fri, 16 Aug 2024 14:49:32 +1200 Subject: [PATCH] Revert "perf: implement `weakAtomFamily` (#117)" This reverts commit ceb8723ac3babcb2db4172a934f5bbeb3cd6f798. --- .changeset/tiny-worms-warn.md | 5 -- packages/react/src/stores/accounts.ts | 6 +- packages/react/src/stores/block.ts | 7 +-- packages/react/src/stores/client.ts | 8 +-- packages/react/src/stores/query.ts | 8 +-- packages/react/src/utils/jotai.ts | 90 ++------------------------- 6 files changed, 20 insertions(+), 104 deletions(-) delete mode 100644 .changeset/tiny-worms-warn.md diff --git a/.changeset/tiny-worms-warn.md b/.changeset/tiny-worms-warn.md deleted file mode 100644 index 35684b44..00000000 --- a/.changeset/tiny-worms-warn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@reactive-dot/react": patch ---- - -Implemented `atomFamily` using `WeakMap`: this change allows for the automatic garbage collection of unused values, improving memory efficiency and overall performance. diff --git a/packages/react/src/stores/accounts.ts b/packages/react/src/stores/accounts.ts index 158d4889..3b775c04 100644 --- a/packages/react/src/stores/accounts.ts +++ b/packages/react/src/stores/accounts.ts @@ -1,4 +1,4 @@ -import { weakAtomFamily, withAtomFamilyErrorCatcher } from "../utils/jotai.js"; +import { withAtomFamilyErrorCatcher } from "../utils/jotai.js"; import { chainSpecDataAtomFamily } from "./client.js"; import { walletsAtom } from "./wallets.js"; import { @@ -7,9 +7,9 @@ import { type PolkadotAccount, } from "@reactive-dot/core"; import type { Atom } from "jotai"; -import { atomWithObservable } from "jotai/utils"; +import { atomFamily, atomWithObservable } from "jotai/utils"; -export const accountsAtom = weakAtomFamily( +export const accountsAtom = atomFamily( (chainId: ChainId): Atom> => withAtomFamilyErrorCatcher( accountsAtom, diff --git a/packages/react/src/stores/block.ts b/packages/react/src/stores/block.ts index 8928366c..d853abf8 100644 --- a/packages/react/src/stores/block.ts +++ b/packages/react/src/stores/block.ts @@ -1,11 +1,10 @@ -import { weakAtomFamily } from "../utils/jotai.js"; import { clientAtomFamily } from "./client.js"; import { type ChainId, getBlock } from "@reactive-dot/core"; -import { atomWithObservable } from "jotai/utils"; +import { atomFamily, atomWithObservable } from "jotai/utils"; import { from } from "rxjs"; import { switchMap } from "rxjs/operators"; -export const finalizedBlockAtomFamily = weakAtomFamily((chainId: ChainId) => +export const finalizedBlockAtomFamily = atomFamily((chainId: ChainId) => atomWithObservable((get) => from(get(clientAtomFamily(chainId))).pipe( switchMap((client) => getBlock(client, { tag: "finalized" })), @@ -13,7 +12,7 @@ export const finalizedBlockAtomFamily = weakAtomFamily((chainId: ChainId) => ), ); -export const bestBlockAtomFamily = weakAtomFamily((chainId: ChainId) => +export const bestBlockAtomFamily = atomFamily((chainId: ChainId) => atomWithObservable((get) => from(get(clientAtomFamily(chainId))).pipe( switchMap((client) => getBlock(client, { tag: "best" })), diff --git a/packages/react/src/stores/client.ts b/packages/react/src/stores/client.ts index 6b105f03..c438d8d1 100644 --- a/packages/react/src/stores/client.ts +++ b/packages/react/src/stores/client.ts @@ -1,10 +1,10 @@ -import { weakAtomFamily } from "../utils/jotai.js"; import { chainConfigsAtom } from "./config.js"; import type { ChainId } from "@reactive-dot/core"; import { getClient, ReDotError } from "@reactive-dot/core"; import { atom } from "jotai"; +import { atomFamily } from "jotai/utils"; -export const clientAtomFamily = weakAtomFamily((chainId: ChainId) => +export const clientAtomFamily = atomFamily((chainId: ChainId) => atom(async (get) => { const chainConfig = get(chainConfigsAtom)[chainId]; @@ -16,7 +16,7 @@ export const clientAtomFamily = weakAtomFamily((chainId: ChainId) => }), ); -export const chainSpecDataAtomFamily = weakAtomFamily((chainId: ChainId) => +export const chainSpecDataAtomFamily = atomFamily((chainId: ChainId) => atom(async (get) => { const client = await get(clientAtomFamily(chainId)); @@ -24,7 +24,7 @@ export const chainSpecDataAtomFamily = weakAtomFamily((chainId: ChainId) => }), ); -export const typedApiAtomFamily = weakAtomFamily((chainId: ChainId) => +export const typedApiAtomFamily = atomFamily((chainId: ChainId) => atom(async (get) => { const config = get(chainConfigsAtom)[chainId]; diff --git a/packages/react/src/stores/query.ts b/packages/react/src/stores/query.ts index 189e703b..2b75d2e7 100644 --- a/packages/react/src/stores/query.ts +++ b/packages/react/src/stores/query.ts @@ -1,4 +1,4 @@ -import { weakAtomFamily, withAtomFamilyErrorCatcher } from "../utils/jotai.js"; +import { withAtomFamilyErrorCatcher } from "../utils/jotai.js"; import { stringify } from "../utils/vanilla.js"; import { typedApiAtomFamily } from "./client.js"; import { @@ -10,10 +10,10 @@ import { type QueryInstruction, } from "@reactive-dot/core"; import { atom, type Atom, type WritableAtom } from "jotai"; -import { atomWithObservable, atomWithRefresh } from "jotai/utils"; +import { atomFamily, atomWithObservable, atomWithRefresh } from "jotai/utils"; import { from, switchMap, type Observable } from "rxjs"; -const instructionPayloadAtomFamily = weakAtomFamily( +const instructionPayloadAtomFamily = atomFamily( (param: { chainId: ChainId; instruction: Exclude< @@ -76,7 +76,7 @@ export function getQueryInstructionPayloadAtoms( // TODO: should be memoized within render function instead // https://github.com/pmndrs/jotai/discussions/1553 -export const queryPayloadAtomFamily = weakAtomFamily( +export const queryPayloadAtomFamily = atomFamily( (param: { chainId: ChainId; query: Query }): Atom => withAtomFamilyErrorCatcher( queryPayloadAtomFamily, diff --git a/packages/react/src/utils/jotai.ts b/packages/react/src/utils/jotai.ts index 3df81670..b3314f0f 100644 --- a/packages/react/src/utils/jotai.ts +++ b/packages/react/src/utils/jotai.ts @@ -1,87 +1,12 @@ import { QueryError } from "@reactive-dot/core"; import type { Atom, Getter } from "jotai"; +import type { AtomFamily } from "jotai/vanilla/utils/atomFamily"; import { Observable } from "rxjs"; import { catchError } from "rxjs/operators"; -type WeakAtomFamily = ((param: TParam) => TAtomType) & { - remove: (param: TParam) => void; -}; - -export function weakAtomFamily>( - initializeAtom: (param: TParam) => TAtomType, - areEqual?: (a: TParam, b: TParam) => boolean, -): WeakAtomFamily { - // TODO: should use `Symbol` as `WeakMap` key instead - // https://bugzilla.mozilla.org/show_bug.cgi?id=1710433 - const keys = new Map(); - const atoms = new WeakMap(); - - const getKey = (param: TParam) => { - if (areEqual === undefined) { - return keys.get(param); - } - - for (const [key, value] of keys) { - if (areEqual(key, param)) { - return value; - } - } - - return undefined; - }; - - const deleteKey = (param: TParam) => { - if (areEqual === undefined) { - return keys.delete(param); - } - - for (const key of keys.keys()) { - if (areEqual(key, param)) { - return keys.delete(param); - } - } - - return false; - }; - - return Object.assign( - (param: TParam) => { - const key = getKey(param); - - if (key !== undefined) { - const atom = atoms.get(key); - - if (atom !== undefined) { - return atom; - } - } - - const newKey = {}; - keys.set(param, newKey); - - const newAtom = initializeAtom(param); - atoms.set(newKey, newAtom); - - return newAtom; - }, - { - remove: (param: TParam) => { - const key = getKey(param); - - if (key === undefined) { - return; - } - - deleteKey(param); - atoms.delete(key); - }, - }, - ); -} - export class AtomFamilyError extends QueryError { constructor( - readonly atomFamily: WeakAtomFamily, + readonly atomFamily: AtomFamily, readonly param: unknown, message: string | undefined, options?: ErrorOptions, @@ -89,14 +14,11 @@ export class AtomFamilyError extends QueryError { super(message, options); } - static fromAtomFamilyError< - TError, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - TAtomFamily extends WeakAtomFamily, - >( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static fromAtomFamilyError>( error: TError, atomFamily: TAtomFamily, - param: TAtomFamily extends WeakAtomFamily + param: TAtomFamily extends AtomFamily ? Param : unknown, message?: string, @@ -114,7 +36,7 @@ export function withAtomFamilyErrorCatcher< // eslint-disable-next-line @typescript-eslint/no-explicit-any TAtomCreator extends (read: TRead, ...args: any[]) => Atom, >( - atomFamily: WeakAtomFamily, + atomFamily: AtomFamily, param: TParam, atomCreator: TAtomCreator, ): TAtomCreator {