mirror of
https://github.com/zhigang1992/mobx-utils.git
synced 2026-01-12 17:32:29 +08:00
feat: Add onCleanup for computedFn (#297)
This commit is contained in:
@@ -8,6 +8,10 @@ import {
|
||||
isAction,
|
||||
} from "mobx"
|
||||
|
||||
export type IComputedFnOptions<F extends (...args: any[]) => any> = {
|
||||
onCleanup?: (result: ReturnType<F> | undefined, ...args: Parameters<F>) => void
|
||||
} & IComputedValueOptions<ReturnType<F>>
|
||||
|
||||
/**
|
||||
* computedFn takes a function with an arbitrary amount of arguments,
|
||||
* and memoizes the output of the function based on the arguments passed in.
|
||||
@@ -44,7 +48,7 @@ import {
|
||||
*/
|
||||
export function computedFn<T extends (...args: any[]) => any>(
|
||||
fn: T,
|
||||
keepAliveOrOptions: IComputedValueOptions<ReturnType<T>> | boolean = false
|
||||
keepAliveOrOptions: IComputedFnOptions<T> | boolean = false
|
||||
): T {
|
||||
if (isAction(fn)) throw new Error("computedFn shouldn't be used on actions")
|
||||
|
||||
@@ -56,7 +60,7 @@ export function computedFn<T extends (...args: any[]) => any>(
|
||||
: keepAliveOrOptions
|
||||
const d = new DeepMap<IComputedValue<any>>()
|
||||
|
||||
return function (this: any, ...args: any[]): any {
|
||||
return function (this: any, ...args: Parameters<T>): ReturnType<T> {
|
||||
const entry = d.entry(args)
|
||||
// cache hit, return
|
||||
if (entry.exists()) return entry.get().get()
|
||||
@@ -71,9 +75,10 @@ export function computedFn<T extends (...args: any[]) => any>(
|
||||
return fn.apply(this, args)
|
||||
}
|
||||
// create new entry
|
||||
let latestValue: ReturnType<T> | undefined
|
||||
const c = computed(
|
||||
() => {
|
||||
return fn.apply(this, args)
|
||||
return (latestValue = fn.apply(this, args))
|
||||
},
|
||||
{
|
||||
...opts,
|
||||
@@ -85,6 +90,8 @@ export function computedFn<T extends (...args: any[]) => any>(
|
||||
if (!opts.keepAlive)
|
||||
onBecomeUnobserved(c, () => {
|
||||
d.entry(args).delete()
|
||||
if (opts.onCleanup) opts.onCleanup(latestValue, ...args)
|
||||
latestValue = undefined
|
||||
})
|
||||
// return current val
|
||||
return c.get()
|
||||
|
||||
@@ -175,6 +175,22 @@ test("supports options", () => {
|
||||
expect(events).toEqual([])
|
||||
})
|
||||
|
||||
test("supports onCleanup", () => {
|
||||
const sep = observable.box(".")
|
||||
const unloaded = []
|
||||
const joinedStr = computedFn((sep) => [1, 2, 3].join(sep), {
|
||||
onCleanup: (result, sep) => unloaded.push([result, sep]),
|
||||
})
|
||||
autorun(() => joinedStr(sep.get()))
|
||||
sep.set(",")
|
||||
expect(unloaded.length).toBe(1)
|
||||
sep.set(" ")
|
||||
expect(unloaded).toEqual([
|
||||
["1.2.3", "."],
|
||||
["1,2,3", ","],
|
||||
])
|
||||
})
|
||||
|
||||
test("should not allow actions", () => {
|
||||
expect(() => computedFn(action(() => {}))).toThrow("action")
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user