From 2ad4bb4d8097da7705d9434d264cc12c6c3741d7 Mon Sep 17 00:00:00 2001 From: Ben Durrant Date: Wed, 27 Nov 2024 21:29:33 +0000 Subject: [PATCH] Update to new version of upsert proposal --- packages/toolkit/src/combineSlices.ts | 10 +- packages/toolkit/src/createSlice.ts | 40 ++++---- .../toolkit/src/dynamicMiddleware/index.ts | 4 +- packages/toolkit/src/utils.ts | 93 +++++-------------- 4 files changed, 53 insertions(+), 94 deletions(-) diff --git a/packages/toolkit/src/combineSlices.ts b/packages/toolkit/src/combineSlices.ts index 26ff173e7d..a5353af937 100644 --- a/packages/toolkit/src/combineSlices.ts +++ b/packages/toolkit/src/combineSlices.ts @@ -8,7 +8,7 @@ import type { UnionToIntersection, WithOptionalProp, } from './tsHelpers' -import { emplace } from './utils' +import { getOrInsertComputed } from './utils' type SliceLike = { reducerPath: ReducerPath @@ -324,8 +324,10 @@ const createStateProxy = ( state: State, reducerMap: Partial>, ) => - emplace(stateProxyMap, state, { - insert: () => + getOrInsertComputed( + stateProxyMap, + state, + () => new Proxy(state, { get: (target, prop, receiver) => { if (prop === ORIGINAL_STATE) return target @@ -350,7 +352,7 @@ const createStateProxy = ( return result }, }), - }) as State + ) as State const original = (state: any) => { if (!isStateProxy(state)) { diff --git a/packages/toolkit/src/createSlice.ts b/packages/toolkit/src/createSlice.ts index e3d2c25c56..1d4f3e3712 100644 --- a/packages/toolkit/src/createSlice.ts +++ b/packages/toolkit/src/createSlice.ts @@ -26,7 +26,7 @@ import { createReducer } from './createReducer' import type { ActionReducerMapBuilder, TypedActionCreator } from './mapBuilders' import { executeReducerBuilderCallback } from './mapBuilders' import type { Id, TypeGuard } from './tsHelpers' -import { emplace } from './utils' +import { getOrInsertComputed } from './utils' const asyncThunkSymbol = /* @__PURE__ */ Symbol.for( 'rtk-slice-createasyncthunk', @@ -769,25 +769,25 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) { function getSelectors( selectState: (rootState: any) => State = selectSelf, ) { - const selectorCache = emplace(injectedSelectorCache, injected, { - insert: () => new WeakMap(), - }) - - return emplace(selectorCache, selectState, { - insert: () => { - const map: Record> = {} - for (const [name, selector] of Object.entries( - options.selectors ?? {}, - )) { - map[name] = wrapSelector( - selector, - selectState, - getInitialState, - injected, - ) - } - return map - }, + const selectorCache = getOrInsertComputed( + injectedSelectorCache, + injected, + () => new WeakMap(), + ) + + return getOrInsertComputed(selectorCache, selectState, () => { + const map: Record> = {} + for (const [name, selector] of Object.entries( + options.selectors ?? {}, + )) { + map[name] = wrapSelector( + selector, + selectState, + getInitialState, + injected, + ) + } + return map }) as any } return { diff --git a/packages/toolkit/src/dynamicMiddleware/index.ts b/packages/toolkit/src/dynamicMiddleware/index.ts index ed151b2979..7fc5a60892 100644 --- a/packages/toolkit/src/dynamicMiddleware/index.ts +++ b/packages/toolkit/src/dynamicMiddleware/index.ts @@ -3,7 +3,7 @@ import { compose } from 'redux' import { createAction } from '../createAction' import { isAllOf } from '../matchers' import { nanoid } from '../nanoid' -import { emplace, find } from '../utils' +import { find, getOrInsertComputed } from '../utils' import type { AddMiddleware, DynamicMiddleware, @@ -73,7 +73,7 @@ export const createDynamicMiddleware = < const getFinalMiddleware: Middleware<{}, State, DispatchType> = (api) => { const appliedMiddleware = Array.from(middlewareMap.values()).map((entry) => - emplace(entry.applied, api, { insert: () => entry.middleware(api) }), + getOrInsertComputed(entry.applied, api, entry.middleware), ) return compose(...appliedMiddleware) } diff --git a/packages/toolkit/src/utils.ts b/packages/toolkit/src/utils.ts index 1f8445bb0f..157cc86b08 100644 --- a/packages/toolkit/src/utils.ts +++ b/packages/toolkit/src/utils.ts @@ -87,81 +87,38 @@ export function freezeDraftable(val: T) { return isDraftable(val) ? createNextState(val, () => {}) : val } -interface WeakMapEmplaceHandler { - /** - * Will be called to get value, if no value is currently in map. - */ - insert?(key: K, map: WeakMap): V - /** - * Will be called to update a value, if one exists already. - */ - update?(previous: V, key: K, map: WeakMap): V -} +export function getOrInsert( + map: WeakMap, + key: K, + value: V, +): V +export function getOrInsert(map: Map, key: K, value: V): V +export function getOrInsert( + map: Map | WeakMap, + key: K, + value: V, +): V { + if (map.has(key)) return map.get(key) as V -interface MapEmplaceHandler { - /** - * Will be called to get value, if no value is currently in map. - */ - insert?(key: K, map: Map): V - /** - * Will be called to update a value, if one exists already. - */ - update?(previous: V, key: K, map: Map): V + return map.set(key, value).get(key) as V } -export function emplace( - map: Map, +export function getOrInsertComputed( + map: WeakMap, key: K, - handler: MapEmplaceHandler, + compute: (key: K) => V, ): V -export function emplace( - map: WeakMap, +export function getOrInsertComputed( + map: Map, key: K, - handler: WeakMapEmplaceHandler, + compute: (key: K) => V, ): V -/** - * Allow inserting a new value, or updating an existing one - * @throws if called for a key with no current value and no `insert` handler is provided - * @returns current value in map (after insertion/updating) - * ```ts - * // return current value if already in map, otherwise initialise to 0 and return that - * const num = emplace(map, key, { - * insert: () => 0 - * }) - * - * // increase current value by one if already in map, otherwise initialise to 0 - * const num = emplace(map, key, { - * update: (n) => n + 1, - * insert: () => 0, - * }) - * - * // only update if value's already in the map - and increase it by one - * if (map.has(key)) { - * const num = emplace(map, key, { - * update: (n) => n + 1, - * }) - * } - * ``` - * - * @remarks - * Based on https://github.com/tc39/proposal-upsert currently in Stage 2 - maybe in a few years we'll be able to replace this with direct method calls - */ -export function emplace( - map: WeakMap, +export function getOrInsertComputed( + map: Map | WeakMap, key: K, - handler: WeakMapEmplaceHandler, + compute: (key: K) => V, ): V { - if (map.has(key)) { - let value = map.get(key) as V - if (handler.update) { - value = handler.update(value, key, map) - map.set(key, value) - } - return value - } - if (!handler.insert) - throw new Error('No insert provided for key not already in map') - const inserted = handler.insert(key, map) - map.set(key, inserted) - return inserted + if (map.has(key)) return map.get(key) as V + + return map.set(key, compute(key)).get(key) as V }