mirror of
https://github.com/zhigang1992/mobx-utils.git
synced 2026-06-12 17:08:09 +08:00
have transformer not memorize when called outside reactive context
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { computed, onBecomeUnobserved, IComputedValue } from "mobx"
|
||||
import { computed, onBecomeUnobserved, IComputedValue, _isComputingDerivation } from "mobx"
|
||||
import { invariant, addHiddenProp } from "./utils"
|
||||
|
||||
export type ITransformer<A, B> = (object: A) => B
|
||||
|
||||
export interface ITransformerParams<A, B> {
|
||||
onCleanup?: (resultObject: B | undefined, sourceObject?: A) => void
|
||||
keepAlive?: boolean
|
||||
debugNameGenerator?: (sourceObject?: A) => string
|
||||
}
|
||||
|
||||
@@ -36,19 +37,18 @@ export function createTransformer<A, B>(
|
||||
// Memoizes: object id -> reactive view that applies transformer to the object
|
||||
let views: { [id: number]: IComputedValue<B> } = {}
|
||||
let onCleanup: Function = undefined
|
||||
let keepAlive: boolean = false
|
||||
let debugNameGenerator: Function = undefined
|
||||
if (typeof arg2 === "object") {
|
||||
onCleanup = arg2.onCleanup
|
||||
keepAlive = arg2.keepAlive !== undefined ? arg2.keepAlive : false
|
||||
debugNameGenerator = arg2.debugNameGenerator
|
||||
} else if (typeof arg2 === "function") {
|
||||
onCleanup = arg2
|
||||
}
|
||||
|
||||
function createView(sourceIdentifier: number, sourceObject: A) {
|
||||
let latestValue: B
|
||||
if (typeof arg2 === "object") {
|
||||
onCleanup = arg2.onCleanup
|
||||
debugNameGenerator = arg2.debugNameGenerator
|
||||
} else if (typeof arg2 === "function") {
|
||||
onCleanup = arg2
|
||||
} else {
|
||||
onCleanup = undefined
|
||||
debugNameGenerator = undefined
|
||||
}
|
||||
const prettifiedName = debugNameGenerator
|
||||
? debugNameGenerator(sourceObject)
|
||||
: `Transformer-${(<any>transformer).name}-${sourceIdentifier}`
|
||||
@@ -60,18 +60,33 @@ export function createTransformer<A, B>(
|
||||
name: prettifiedName
|
||||
}
|
||||
)
|
||||
const disposer = onBecomeUnobserved(expr, () => {
|
||||
delete views[sourceIdentifier]
|
||||
disposer()
|
||||
if (onCleanup) onCleanup(latestValue, sourceObject)
|
||||
})
|
||||
if (!keepAlive) {
|
||||
const disposer = onBecomeUnobserved(expr, () => {
|
||||
delete views[sourceIdentifier]
|
||||
disposer()
|
||||
if (onCleanup) onCleanup(latestValue, sourceObject)
|
||||
})
|
||||
}
|
||||
return expr
|
||||
}
|
||||
|
||||
let memoWarned = false
|
||||
return (object: A) => {
|
||||
const identifier = getMemoizationId(object)
|
||||
let reactiveView = views[identifier]
|
||||
if (reactiveView) return reactiveView.get()
|
||||
if (!keepAlive && !_isComputingDerivation()) {
|
||||
if (!memoWarned) {
|
||||
console.warn(
|
||||
"invoking a transformer from outside a reactive context won't memorized " +
|
||||
"and is cleaned up immediately, unless keepAlive is set"
|
||||
)
|
||||
memoWarned = true
|
||||
}
|
||||
const value = transformer(object)
|
||||
if (onCleanup) onCleanup(value, object)
|
||||
return value
|
||||
}
|
||||
// Not in cache; create a reactive view
|
||||
reactiveView = views[identifier] = createView(identifier, object)
|
||||
return reactiveView.get()
|
||||
|
||||
@@ -1354,3 +1354,21 @@ test("should respect debugNameGenerator argument", () => {
|
||||
objectName = m.getObserverTree(state, "title").observers[0].name
|
||||
expect(objectName).toBe("COFFEE-DEBUG")
|
||||
})
|
||||
|
||||
test("should not memorize outside reactive context", () => {
|
||||
let transformer = jest.fn((input: string) => input.toLowerCase())
|
||||
let onCleanup = jest.fn()
|
||||
let stubTransformer = createTransformer(transformer, onCleanup)
|
||||
|
||||
stubTransformer("HELLO")
|
||||
expect(transformer).toHaveBeenCalledWith("HELLO")
|
||||
expect(onCleanup).toHaveBeenCalledWith("hello", "HELLO")
|
||||
transformer.mockClear()
|
||||
onCleanup.mockClear()
|
||||
|
||||
stubTransformer("HELLO")
|
||||
expect(transformer).toHaveBeenCalledWith("HELLO")
|
||||
expect(onCleanup).toHaveBeenCalledWith("hello", "HELLO")
|
||||
transformer.mockClear()
|
||||
onCleanup.mockClear()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user