diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63cfe1d..5ea9067 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## [Unreleased]
+### Added
+
+- breaking: avoid action merging #32
+
## [0.3.0] - 2024-07-11
### Added
diff --git a/docs/getting-started/01_introduction.md b/docs/getting-started/01_introduction.md
index 0075e05..8e722df 100644
--- a/docs/getting-started/01_introduction.md
+++ b/docs/getting-started/01_introduction.md
@@ -59,8 +59,6 @@ By utilizing `create` from Zustand, you can combine these slices into a single s
const useCountStore = create(withSlices(countSlice, textSlice));
```
-> 💡 **Note:** Actions with the same name across slices are merged into a single action in the combined store. Calling such an action executes the corresponding actions from each slice. For example, since both slices have a `reset` action, calling `reset` will reset both `count` and `text` to their initial values.
-
### Easily utilize it in your components
Finally, you can seamlessly integrate and access your store directly into your component logic utilizing the `useCountStore` hook.
diff --git a/examples/01_counter/src/app.tsx b/examples/01_counter/src/app.tsx
index ba3ea44..2ed887f 100644
--- a/examples/01_counter/src/app.tsx
+++ b/examples/01_counter/src/app.tsx
@@ -5,8 +5,8 @@ const countSlice = createSlice({
name: 'count',
value: 0,
actions: {
- inc: () => (prev) => prev + 1,
- reset: () => () => 0,
+ incCount: () => (prev) => prev + 1,
+ resetCount: () => () => 0,
},
});
@@ -15,7 +15,7 @@ const textSlice = createSlice({
value: 'Hello',
actions: {
updateText: (newText: string) => () => newText,
- reset: () => () => 'Hello',
+ resetText: () => () => 'Hello',
},
});
@@ -24,12 +24,17 @@ const useCountStore = create(withSlices(countSlice, textSlice));
const Counter = () => {
const count = useCountStore((state) => state.count);
const text = useCountStore((state) => state.text);
- const { inc, updateText, reset } = useCountStore.getState();
+ const { incCount, resetCount, updateText, resetText } =
+ useCountStore.getState();
+ const reset = () => {
+ resetCount();
+ resetText();
+ };
return (
<>
Count: {count}
-
diff --git a/examples/02_async/src/app.tsx b/examples/02_async/src/app.tsx
index 64673c1..5e25981 100644
--- a/examples/02_async/src/app.tsx
+++ b/examples/02_async/src/app.tsx
@@ -8,8 +8,8 @@ const countSlice = createSlice({
name: 'count',
value: Promise.resolve(0),
actions: {
- inc: () => async (prev) => (await prev) + 1,
- reset: () => async () => 0,
+ incCount: () => async (prev) => (await prev) + 1,
+ resetCount: () => async () => 0,
},
});
@@ -18,7 +18,7 @@ const textSlice = createSlice({
value: 'Hello',
actions: {
updateText: (newText: string) => () => newText,
- reset: () => () => 'Hello',
+ resetText: () => () => 'Hello',
},
});
@@ -27,12 +27,17 @@ const useCountStore = create(withSlices(countSlice, textSlice));
const Counter = () => {
const count = use(useCountStore((state) => state.count));
const text = useCountStore((state) => state.text);
- const { inc, updateText, reset } = useCountStore.getState();
+ const { incCount, resetCount, updateText, resetText } =
+ useCountStore.getState();
+ const reset = () => {
+ resetCount();
+ resetText();
+ };
return (
<>
Count: {count}
-
+
+1
diff --git a/examples/03_actions/src/app.tsx b/examples/03_actions/src/app.tsx
index 863cb7c..f7663e5 100644
--- a/examples/03_actions/src/app.tsx
+++ b/examples/03_actions/src/app.tsx
@@ -5,9 +5,9 @@ const countSlice = createSlice({
name: 'count',
value: 0,
actions: {
- 'count/inc': () => (prev) => prev + 1,
- 'count/set': (newCount: number) => () => newCount,
- reset: () => () => 0,
+ incCount: () => (prev) => prev + 1,
+ setCount: (newCount: number) => () => newCount,
+ resetCount: () => () => 0,
},
});
@@ -15,15 +15,19 @@ const textSlice = createSlice({
name: 'text',
value: 'Hello',
actions: {
- 'text/set': (newText: string) => () => newText,
- reset: () => () => 'Hello',
+ updateText: (newText: string) => () => newText,
+ resetText: () => () => 'Hello',
},
});
const useCountStore = create(
withActions(withSlices(countSlice, textSlice), {
+ reset: () => (state) => {
+ state.resetCount();
+ state.resetText();
+ },
setCountWithTextLength: () => (state) => {
- state['count/set'](state.text.length);
+ state.setCount(state.text.length);
},
}),
);
@@ -31,17 +35,13 @@ const useCountStore = create(
const Counter = () => {
const count = useCountStore((state) => state.count);
const text = useCountStore((state) => state.text);
- const {
- 'count/inc': inc,
- 'text/set': updateText,
- reset,
- setCountWithTextLength,
- } = useCountStore.getState();
+ const { incCount, updateText, reset, setCountWithTextLength } =
+ useCountStore.getState();
return (
<>
Count: {count}
-
+
+1
diff --git a/examples/04_immer/src/app.tsx b/examples/04_immer/src/app.tsx
index 59fbdcd..a3ee13a 100644
--- a/examples/04_immer/src/app.tsx
+++ b/examples/04_immer/src/app.tsx
@@ -8,10 +8,10 @@ const countSlice = createSliceWithImmer({
count: 0,
},
actions: {
- inc: () => (state) => {
+ incCount: () => (state) => {
state.count += 1;
},
- reset: () => () => ({ count: 0 }),
+ resetCount: () => () => ({ count: 0 }),
},
});
@@ -20,7 +20,7 @@ const textSlice = createSliceWithImmer({
value: 'Hello',
actions: {
updateText: (newText: string) => () => newText,
- reset: () => () => 'Hello',
+ resetText: () => () => 'Hello',
},
});
@@ -29,12 +29,17 @@ const useCountStore = create(withSlices(countSlice, textSlice));
const Counter = () => {
const { count } = useCountStore((state) => state.count);
const text = useCountStore((state) => state.text);
- const { inc, updateText, reset } = useCountStore.getState();
+ const { incCount, resetCount, updateText, resetText } =
+ useCountStore.getState();
+ const reset = () => {
+ resetCount();
+ resetText();
+ };
return (
<>
Count: {count}
-
+
+1
diff --git a/src/with-slices.ts b/src/with-slices.ts
index 780d6da..659cbf0 100644
--- a/src/with-slices.ts
+++ b/src/with-slices.ts
@@ -1,9 +1,5 @@
import type { SliceConfig } from './create-slice.js';
-type ParametersIf = T extends (...args: infer Args) => unknown
- ? Args
- : never;
-
type InferState = Configs extends [
SliceConfig,
...infer Rest,
@@ -15,37 +11,17 @@ type InferState = Configs extends [
} & InferState
: unknown;
-type HasDuplicatedNames<
- Configs,
- Names extends string[] = [],
-> = Configs extends [
+type HasDuplicatedNames = Configs extends [
SliceConfig,
...infer Rest,
]
- ? Extract extends never
- ? HasDuplicatedNames
- : true
- : false;
-
-type HasDuplicatedArgs = Configs extends [
- SliceConfig,
- ...infer Rest,
-]
- ? {
- [actionName in keyof State]: ParametersIf;
- } extends {
- [actionName in keyof Actions]: Parameters;
- }
- ? HasDuplicatedArgs
+ ? Extract extends never
+ ? HasDuplicatedNames
: true
: false;
type ValidConfigs =
- HasDuplicatedNames extends true
- ? never
- : HasDuplicatedArgs> extends true
- ? never
- : Configs;
+ HasDuplicatedNames extends true ? never : Configs;
export function withSlices<
Configs extends SliceConfig>[],
@@ -63,29 +39,22 @@ export function withSlices<
) => {
const state: Record = {};
type ActionFn = (...args: unknown[]) => (prev: unknown) => unknown;
- const sliceMapsByAction = new Map>();
for (const config of configs) {
state[config.name] = config.value;
- for (const [actionName, actionFn] of Object.entries(config.actions)) {
- let actionsBySlice = sliceMapsByAction.get(actionName);
- if (!actionsBySlice) {
- sliceMapsByAction.set(actionName, (actionsBySlice = new Map()));
- }
- actionsBySlice.set(config.name, actionFn as never);
- }
- }
- for (const [actionName, actionsBySlice] of sliceMapsByAction) {
- state[actionName] = (...args: unknown[]) => {
- set(((prevState: Record) => {
- const nextState: Record = {};
- for (const [sliceName, actionFn] of actionsBySlice) {
- const prevSlice = prevState[sliceName];
+ for (const [actionName, actionFn] of Object.entries(
+ config.actions,
+ )) {
+ state[actionName] = (...args: unknown[]) => {
+ set(((prevState: Record) => {
+ const prevSlice = prevState[config.name];
const nextSlice = actionFn(...args)(prevSlice);
- nextState[sliceName] = nextSlice;
- }
- return nextState;
- }) as never);
- };
+ if (Object.is(prevSlice, nextSlice)) {
+ return prevState;
+ }
+ return { [config.name]: nextSlice };
+ }) as never);
+ };
+ }
}
return state;
}) as never;
diff --git a/tests/01_basic.spec.tsx b/tests/01_basic.spec.tsx
index 8eabf65..9b99771 100644
--- a/tests/01_basic.spec.tsx
+++ b/tests/01_basic.spec.tsx
@@ -24,8 +24,8 @@ test('withSlices', () => {
name: 'count',
value: 0,
actions: {
- inc: () => (prev) => prev + 1,
- reset: () => () => 0,
+ incCount: () => (prev) => prev + 1,
+ resetCount: () => () => 0,
},
});
const textSlice = createSlice({
@@ -33,7 +33,7 @@ test('withSlices', () => {
value: 'Hello',
actions: {
updateText: (newText: string) => () => newText,
- reset: () => () => 'Hello',
+ resetText: () => () => 'Hello',
},
});
const combinedConfig = withSlices(countSlice, textSlice);
@@ -42,7 +42,8 @@ test('withSlices', () => {
const state = store.getState();
expect(state.count).toBe(countSlice.value);
expect(state.text).toBe(textSlice.value);
- expect(state.inc).toBeInstanceOf(Function);
- expect(state.reset).toBeInstanceOf(Function);
+ expect(state.incCount).toBeInstanceOf(Function);
+ expect(state.resetCount).toBeInstanceOf(Function);
expect(state.updateText).toBeInstanceOf(Function);
+ expect(state.resetText).toBeInstanceOf(Function);
});
diff --git a/tests/02_type.spec.tsx b/tests/02_type.spec.tsx
index dacb990..3a9afee 100644
--- a/tests/02_type.spec.tsx
+++ b/tests/02_type.spec.tsx
@@ -35,8 +35,8 @@ describe('withSlices', () => {
name: 'count',
value: 0,
actions: {
- inc: () => (prev) => prev + 1,
- reset: () => () => 0,
+ incCount: () => (prev) => prev + 1,
+ resetCount: () => () => 0,
},
});
@@ -45,16 +45,17 @@ describe('withSlices', () => {
value: 'Hello',
actions: {
updateText: (newText: string) => () => newText,
- reset: () => () => 'Hello',
+ resetText: () => () => 'Hello',
},
});
type CountTextState = {
count: number;
- inc: () => void;
+ incCount: () => void;
+ resetCount: () => void;
text: string;
updateText: (newText: string) => void;
- reset: () => void;
+ resetText: () => void;
};
const slices = withSlices(countSlice, textSlice);
diff --git a/tests/03_component.spec.tsx b/tests/03_component.spec.tsx
index da11b40..7786c62 100644
--- a/tests/03_component.spec.tsx
+++ b/tests/03_component.spec.tsx
@@ -10,8 +10,8 @@ const countSlice = createSlice({
name: 'count',
value: 0,
actions: {
- inc: () => (state) => state + 1,
- reset: () => () => 0,
+ incCount: () => (state) => state + 1,
+ resetCount: () => () => 0,
},
});
@@ -20,7 +20,7 @@ const textSlice = createSlice({
value: 'Hello',
actions: {
updateText: (text: string) => () => text,
- reset: () => () => 'Hello',
+ resetText: () => () => 'Hello',
},
});
@@ -33,11 +33,11 @@ const renderWithStoreProvider = (app: ReactNode) =>
const Counter = () => {
const count = useSelector((state) => state.count);
- const { inc } = useStoreApi().getState();
+ const { incCount } = useStoreApi().getState();
return (
@@ -55,7 +55,11 @@ const Text = () => {
};
const App = () => {
- const { reset } = useStoreApi().getState();
+ const { resetCount, resetText } = useStoreApi().getState();
+ const reset = () => {
+ resetCount();
+ resetText();
+ };
return (
diff --git a/tests/04_componentWithActions.spec.tsx b/tests/04_componentWithActions.spec.tsx
index ffdea21..55d3ffa 100644
--- a/tests/04_componentWithActions.spec.tsx
+++ b/tests/04_componentWithActions.spec.tsx
@@ -10,8 +10,8 @@ const countSlice = createSlice({
name: 'count',
value: 0,
actions: {
- inc: () => (state) => state + 1,
- set: (newCount: number) => () => newCount,
+ incCount: () => (state) => state + 1,
+ setCount: (newCount: number) => () => newCount,
},
});
@@ -27,7 +27,7 @@ const { StoreProvider, useStoreApi, useSelector } = createZustandContext(() =>
create(
withActions(withSlices(countSlice, textSlice), {
setCountWithTextLength: () => (state) => {
- state.set(state.text.length);
+ state.setCount(state.text.length);
},
}),
),
@@ -38,11 +38,11 @@ const renderWithStoreProvider = (app: ReactNode) =>
const Counter = () => {
const count = useSelector((state) => state.count);
- const { inc } = useStoreApi().getState();
+ const { incCount } = useStoreApi().getState();
return (