Skip to content

Commit 87b85c6

Browse files
authored
Merge pull request #120 from udecode/fix/function-options-handling
2 parents 5285dd4 + 01e0f8c commit 87b85c6

File tree

3 files changed

+50
-3
lines changed

3 files changed

+50
-3
lines changed

.changeset/fix-function-options.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'zustand-x': patch
3+
---
4+
5+
Fix assigning function-valued options so callbacks still work.

packages/zustand-x/src/internal/createBaseApi.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { StoreMutatorIdentifier } from 'zustand';
22

33
import { TBaseStateApiForBuilder, TStoreApiGet } from '../types/baseStore';
4-
import { TState } from '../types/utils';
5-
import { TCreatedStoreMutateType } from '../types/utils';
4+
import { TCreatedStoreMutateType, TState } from '../types/utils';
65

76
import type {
87
AnyFunction,
@@ -44,7 +43,13 @@ export const createBaseApi = <
4443
const typedKey = key as keyof StateType;
4544
const prevValue = store.getState()[typedKey];
4645

47-
if (typeof value === 'function') {
46+
const shouldInvokeUpdater =
47+
typeof value === 'function' &&
48+
prevValue !== undefined &&
49+
prevValue !== null &&
50+
typeof prevValue !== 'function';
51+
52+
if (shouldInvokeUpdater) {
4853
value = value(prevValue);
4954
}
5055
if (prevValue === value) return;

packages/zustand-x/src/tests/createStore.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,43 @@ describe('zustandX', () => {
126126
expect(starsListener).toHaveBeenCalledTimes(2);
127127
expect(starsListener).toHaveBeenLastCalledWith(3, 0);
128128
});
129+
130+
it('should support callback updates for primitive values', () => {
131+
expect(store.get('stars')).toBe(0);
132+
store.set('stars', (currentStars) => currentStars + 5);
133+
expect(store.get('stars')).toBe(5);
134+
store.set('state', {
135+
name: 'zustandX',
136+
stars: 0,
137+
});
138+
});
139+
140+
it('should store function values without invoking them', () => {
141+
type FunctionStoreState = {
142+
handler: () => string;
143+
optionalHandler: ((value: number) => number) | null;
144+
};
145+
146+
const functionStore = createStore<FunctionStoreState>(
147+
{
148+
handler: () => 'initial',
149+
optionalHandler: null,
150+
},
151+
{
152+
name: 'function-store',
153+
}
154+
);
155+
156+
const newHandler = vi.fn(() => 'updated');
157+
expect(() => functionStore.set('handler', newHandler)).not.toThrow();
158+
expect(functionStore.get('handler')).toBe(newHandler);
159+
expect(newHandler).not.toHaveBeenCalled();
160+
161+
const nextOptional = vi.fn((value: number) => value * 2);
162+
functionStore.set('optionalHandler', nextOptional);
163+
expect(functionStore.get('optionalHandler')).toBe(nextOptional);
164+
expect(nextOptional).not.toHaveBeenCalled();
165+
});
129166
});
130167

131168
describe('when using hooks', () => {

0 commit comments

Comments
 (0)