Skip to content

Commit e97efe0

Browse files
committed
fix(slice): fix slice mode
1 parent 9147cf8 commit e97efe0

File tree

4 files changed

+44
-21
lines changed

4 files changed

+44
-21
lines changed

packages/core/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ const useStore = create((set) => ({
2727

2828
### Store Shape Mode (`sliceMode`)
2929

30-
`create()` uses `sliceMode: 'auto'` by default. `auto` only handles unambiguous
31-
inputs. If you pass an object whose enumerable values are all functions, you
32-
must set `sliceMode` explicitly because that shape can mean either a plain store
33-
or slices.
30+
`create()` uses `sliceMode: 'auto'` by default. For backward compatibility,
31+
`auto` still treats a non-empty object whose enumerable values are all
32+
functions as slices. That shape is ambiguous with a plain store that only
33+
contains methods, so you should set `sliceMode` explicitly.
3434

3535
You can force behavior explicitly:
3636

packages/core/src/create.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ import { wrapStore } from './wrapStore';
2020
import { handleMainTransport } from './handleMainTransport';
2121

2222
const namespaceMap = new Map<string, boolean>();
23+
let hasWarnedAmbiguousFunctionMap = false;
24+
25+
const warnAmbiguousFunctionMap = () => {
26+
if (
27+
hasWarnedAmbiguousFunctionMap ||
28+
process.env.NODE_ENV === 'production' ||
29+
process.env.NODE_ENV === 'test'
30+
) {
31+
return;
32+
}
33+
hasWarnedAmbiguousFunctionMap = true;
34+
console.warn(
35+
`sliceMode: 'auto' inferred slices from an object of functions. This shape is ambiguous with a single store that only contains methods. Set sliceMode to 'slices' or 'single' explicitly.`
36+
);
37+
};
2338

2439
/**
2540
* Create a simple store or a shared store. The shared store can be used in a worker or another thread.
@@ -124,9 +139,8 @@ export const create: Creator = <T extends CreateState>(
124139
return true;
125140
}
126141
if (isFunctionMapObject()) {
127-
throw new Error(
128-
`sliceMode: 'auto' cannot infer whether an object of functions is a single store or slices. Please set sliceMode to 'single' or 'slices'.`
129-
);
142+
warnAmbiguousFunctionMap();
143+
return true;
130144
}
131145
return false;
132146
};

packages/core/src/interface.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ export type StoreOptions<T extends CreateState> = {
206206
enablePatches?: boolean;
207207
/**
208208
* control how createState should be interpreted.
209-
* - auto: infer only unambiguous shapes. Object maps whose values are all
210-
* functions must set `sliceMode` explicitly.
209+
* - auto: infer from createState shape. Object maps whose values are all
210+
* functions are ambiguous, so prefer setting `sliceMode` explicitly.
211211
* - slices: force slices mode.
212212
* - single: force single-store mode.
213213
*/
@@ -222,8 +222,8 @@ export type ClientStoreOptions<T extends CreateState> = {
222222
middlewares?: Middleware<T>[];
223223
/**
224224
* control how createState should be interpreted.
225-
* - auto: infer only unambiguous shapes. Object maps whose values are all
226-
* functions must set `sliceMode` explicitly.
225+
* - auto: infer from createState shape. Object maps whose values are all
226+
* functions are ambiguous, so prefer setting `sliceMode` explicitly.
227227
* - slices: force slices mode.
228228
* - single: force single-store mode.
229229
*/

packages/core/test/index.test.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -435,16 +435,25 @@ describe('Store Name Lifecycle', () => {
435435
});
436436

437437
describe('sliceMode', () => {
438-
test('auto mode rejects ambiguous function maps without invoking them', () => {
439-
const ping = jest.fn(() => ({ ok: true }));
440-
expect(() =>
441-
create({
442-
ping
443-
} as any)
444-
).toThrow(
445-
"sliceMode: 'auto' cannot infer whether an object of functions is a single store or slices. Please set sliceMode to 'single' or 'slices'."
446-
);
447-
expect(ping).not.toHaveBeenCalled();
438+
test('auto mode preserves slices inference and warns in development', () => {
439+
const prev = process.env.NODE_ENV;
440+
process.env.NODE_ENV = 'development';
441+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
442+
try {
443+
const useStore = create({
444+
counter: () => ({
445+
count: 0
446+
})
447+
});
448+
expect(useStore.isSliceStore).toBe(true);
449+
expect(useStore.getState().counter.count).toBe(0);
450+
expect(warn).toHaveBeenCalledWith(
451+
"sliceMode: 'auto' inferred slices from an object of functions. This shape is ambiguous with a single store that only contains methods. Set sliceMode to 'slices' or 'single' explicitly."
452+
);
453+
} finally {
454+
warn.mockRestore();
455+
process.env.NODE_ENV = prev;
456+
}
448457
});
449458

450459
test('single mode treats function maps as a plain store', () => {

0 commit comments

Comments
 (0)