diff --git a/src/create-transformer.ts b/src/create-transformer.ts
index 7b6dccb..6e8b1cb 100644
--- a/src/create-transformer.ts
+++ b/src/create-transformer.ts
@@ -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 = (object: A) => B
export interface ITransformerParams {
onCleanup?: (resultObject: B | undefined, sourceObject?: A) => void
+ keepAlive?: boolean
debugNameGenerator?: (sourceObject?: A) => string
}
@@ -36,19 +37,18 @@ export function createTransformer(
// Memoizes: object id -> reactive view that applies transformer to the object
let views: { [id: number]: IComputedValue } = {}
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-${(transformer).name}-${sourceIdentifier}`
@@ -60,18 +60,33 @@ export function createTransformer(
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()
diff --git a/test/create-transformer.ts b/test/create-transformer.ts
index b50c835..23f0740 100644
--- a/test/create-transformer.ts
+++ b/test/create-transformer.ts
@@ -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()
+})