Skip to content

Commit

Permalink
feat: add ability to get previous value from derived fn
Browse files Browse the repository at this point in the history
  • Loading branch information
crutchcorn committed Nov 27, 2024
1 parent cc48436 commit 709f04b
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 19 deletions.
34 changes: 20 additions & 14 deletions packages/store/src/derived.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { Store } from './store'
import type { Listener } from './types'

export interface DerivedFnProps<TState> {
// `undefined` if it's the first run
prevDepVals: Array<any> | undefined
// Can't have currVal, as it's being evaluated from the current derived fn
prevVal: TState | undefined
currDepVals: Array<any>
}

export interface DerivedOptions<TState> {
onSubscribe?: (
listener: Listener<TState>,
Expand All @@ -18,11 +26,7 @@ export interface DerivedOptions<TState> {
*
* @todo Improve the typings to match `deps` from above
*/
fn: (props: {
// `undefined` if it's the first run
prevVals: Array<any> | undefined
currentVals: Array<any>
}) => TState
fn: (props: DerivedFnProps<TState>) => TState
}

export class Derived<TState> {
Expand Down Expand Up @@ -66,16 +70,17 @@ export class Derived<TState> {
storeToDerived = new Map<Store<unknown>, Set<Derived<unknown>>>()
derivedToStore = new Map<Derived<unknown>, Set<Store<unknown>>>()

getDepVals = () => {
const prevVals = [] as Array<unknown>
const currentVals = [] as Array<unknown>
getDepVals = (): DerivedFnProps<TState> => {
const prevDepVals = [] as Array<unknown>
const currDepVals = [] as Array<unknown>
for (const dep of this.options.deps) {
prevVals.push(dep.prevState)
currentVals.push(dep.state)
prevDepVals.push(dep.prevState)
currDepVals.push(dep.state)
}
return {
prevVals,
currentVals,
prevDepVals,
currDepVals,
prevVal: this._store?.prevState ?? undefined,
}
}

Expand All @@ -84,8 +89,9 @@ export class Derived<TState> {
const initVal = options.lazy
? (undefined as ReturnType<typeof options.fn>)
: options.fn({
prevVals: undefined,
currentVals: this.getDepVals().currentVals,
prevDepVals: undefined,
prevVal: undefined,
currDepVals: this.getDepVals().currDepVals,
})

this._store = new Store(initVal, {
Expand Down
34 changes: 29 additions & 5 deletions packages/store/tests/derived.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,21 +214,45 @@ describe('Derived', () => {
expect(fn).toBeCalledWith({ prevVal: 24, currentVal: 48 })
})

test('derivedFn should receive old and new values', () => {
test('derivedFn should receive old and new dep values', () => {
const count = new Store(12)
const date = new Date()
const time = new Store(date)
const fn = vi.fn()
const derived = new Derived({
deps: [count, time],
fn: ({ prevVals, currentVals }) => {
fn({ prevVals, currentVals })
fn: ({ prevDepVals, currDepVals }) => {
fn({ prevDepVals, currDepVals })
return void 0
},
})
derived.mount()
expect(fn).toBeCalledWith({ prevVals: undefined, currentVals: [12, date] })
expect(fn).toBeCalledWith({
prevDepVals: undefined,
currDepVals: [12, date],
})
count.setState(() => 24)
expect(fn).toBeCalledWith({
prevDepVals: [12, date],
currDepVals: [24, date],
})
})

test('derivedFn should receive the old value', () => {
const count = new Store(12)
const date = new Date()
const time = new Store(date)
const fn = vi.fn()
const derived = new Derived({
deps: [count, time],
fn: ({ prevVal }) => {
fn(prevVal)
return count.state
},
})
derived.mount()
expect(fn).toBeCalledWith(undefined)
count.setState(() => 24)
expect(fn).toBeCalledWith({ prevVals: [12, date], currentVals: [24, date] })
expect(fn).toBeCalledWith(12)
})
})

0 comments on commit 709f04b

Please sign in to comment.