@@ -22,7 +22,7 @@ export type ComputedStateOpts<T> = (
2222 * should be *fast* - it determines whether or not you need to
2323 * recompute.
2424 */
25- shouldRecompute ?: ( state : T , nextState : T ) => boolean
25+ shouldRecompute ?: ( state : T , nextState : T | Partial < T > ) => boolean
2626 }
2727) & {
2828 /**
@@ -76,18 +76,14 @@ type ComputedStateImpl = <T extends object, A extends object>(
7676 opts ?: ComputedStateOpts < T > ,
7777) => ( f : StateCreator < T , [ ] , [ ] > ) => StateCreator < T , [ ] , [ ] , T & A >
7878
79- type SetStateWithArgs = Parameters < ReturnType < ReturnType < ComputedStateImpl > > > [ 0 ] extends ( ...args : infer U ) => void
80- ? ( ...args : [ ...U , ...unknown [ ] ] ) => void
81- : never
82-
8379const computedImpl : ComputedStateImpl = ( compute , opts ) => ( f ) => {
8480 type T = ReturnType < typeof f >
8581 type A = ReturnType < typeof compute >
8682
8783 const optsKeys = ! opts || ! ( "keys" in opts ) || opts . keys == null ? undefined : opts . keys
8884 const keysSet = optsKeys ? new Set ( optsKeys as string [ ] ) : undefined
8985
90- function defaultShouldRecomputeFn < T > ( _ : T , nextState : T ) : boolean {
86+ function defaultShouldRecomputeFn < T > ( _ : T , nextState : T | Partial < T > ) : boolean {
9187 if ( ! keysSet || nextState == null ) return true
9288 return Object . keys ( nextState ) . some ( ( k ) => keysSet . has ( k ) )
9389 }
@@ -99,39 +95,49 @@ const computedImpl: ComputedStateImpl = (compute, opts) => (f) => {
9995 return ( set , get , api ) => {
10096 const equalityFn = opts ?. equalityFn ?? shallow
10197
102- const computeAndMerge = ( state : T | ( T & A ) ) : T & A => {
98+ function computeAndMerge ( state : T | ( T & A ) ) : T & A {
10399 // Calculate the new computed state.
104- const computedState : A = compute ( { ... state } )
100+ const computedState = compute ( state )
105101
106102 // If part of the computed state did not change according to the equalityFn,
107103 // then delete that key from the newly calculated computed state.
108104 for ( const k of Object . keys ( computedState ) as ( keyof A ) [ ] ) {
109- if ( equalityFn ( computedState [ k ] , ( state as T & A ) [ k ] ) ) {
105+ if ( k in state && equalityFn ( computedState [ k ] , ( state as T & A ) [ k ] ) ) {
110106 delete computedState [ k ]
111107 }
112108 }
113109
114- return { ... state , ... computedState }
110+ return Object . assign ( state , computedState )
115111 }
116112
113+ const _api = api as Mutate < StoreApi < T > , [ [ "chrisvander/zustand-computed" , A ] ] >
114+
117115 /**
118116 * Higher level function to handle compute & compare overhead.
119117 */
120- const setWithComputed = ( update : T | ( ( state : T ) => T ) , replace ?: boolean , ...args : unknown [ ] ) => {
121- ; ( set as SetStateWithArgs ) (
122- ( state : T ) : T & A => {
123- const updated = typeof update === "object" ? update : update ( state )
124- if ( ! shouldRecomputeFn ?.( state , updated ) ) return { ...state , ...updated } as T & A
125- return computeAndMerge ( { ...state , ...updated } )
126- } ,
127- replace ,
128- ...args ,
129- )
118+ function setState ( partial : T | Partial < T > | ( ( state : T ) => T | Partial < T > ) , replace ?: false ) : void
119+ function setState ( state : T | ( ( state : T ) => T ) , replace : true ) : void
120+ function setState ( arg : T | Partial < T > | ( ( state : T ) => T | Partial < T > ) , replace ?: boolean ) : void {
121+ if ( replace === false || replace == null ) {
122+ // Merge the partial state with the current state.
123+ set ( ( state ) => {
124+ const newState = typeof arg === "function" ? arg ( state ) : arg
125+ if ( ! shouldRecomputeFn ( state , newState ) ) return newState
126+ return computeAndMerge ( Object . assign ( state , newState ) )
127+ } , replace )
128+ return
129+ }
130+
131+ set ( ( state ) => {
132+ const newArg = arg as T | ( ( state : T ) => T )
133+ const newState : T = typeof newArg === "function" ? newArg ( state ) : newArg
134+ if ( ! shouldRecomputeFn ( state , newState ) ) return newState
135+ return computeAndMerge ( newState )
136+ } , replace )
130137 }
131138
132- const _api = api as Mutate < StoreApi < T > , [ [ "chrisvander/zustand-computed" , A ] ] >
133- _api . setState = setWithComputed
134- const st = f ( setWithComputed , get , _api ) as T & A
139+ _api . setState = setState
140+ const st = f ( setState , get , _api )
135141 return Object . assign ( { } , st , compute ( st ) )
136142 }
137143}
0 commit comments