Cross-slice action does not correctly update state with immer middleware #1978
-
For a store with multiple slices, trying to update one slice from another using an action does not seem to work correctly with the immer middleware. Specifically, when I try to change an array, the changes don't seem to get saved. Full example (also available in this sandbox): import { StateCreator } from "zustand";
import { immer } from "zustand/middleware/immer";
import { devtools } from "zustand/middleware";
import { create } from "zustand";
import { shallow } from "zustand/shallow";
import { useEffect } from "react";
interface CounterState {
A: number[];
B: number[];
}
const initialState: CounterState = {
A: [],
B: []
};
interface CounterActions {
pushA: (val: number) => void;
pushB: (val: number) => void;
}
type CounterSlice = CounterState & CounterActions;
const createCounterSlice: StateCreator<
CounterSlice,
[["zustand/immer", never], never],
[],
CounterSlice
> = (set, get) => ({
...initialState,
pushA: (val: number) =>
set((state) => {
state.A.push(val);
}),
pushB: (val: number) =>
set((state) => {
state.B.push(val);
})
});
interface OtherState {}
interface OtherActions {
pushAIndirectly: (val: number) => void;
pushBDirectly: (val: number) => void;
}
type OtherSlice = OtherState & OtherActions;
const createOtherSlice: StateCreator<
OtherSlice & CounterSlice,
[["zustand/immer", never], never],
[],
OtherSlice
> = (set, get) => ({
pushAIndirectly: (val: number) =>
set((state) => {
state.pushA(val);
}),
pushBDirectly: (val: number) =>
set((state) => {
state.B.push(val);
})
});
export const useStore = create<CounterSlice & OtherSlice>()(
devtools(
immer((...a) => ({
...createOtherSlice(...a),
...createCounterSlice(...a)
}))
)
);
export default function App() {
const { A, B, pushAIndirectly, pushBDirectly } = useStore(
(state) => ({
A: state.A,
B: state.B,
pushAIndirectly: state.pushAIndirectly,
pushBDirectly: state.pushBDirectly
}),
shallow
);
useEffect(() => {
pushAIndirectly(2);
pushBDirectly(5);
}, [pushAIndirectly, pushBDirectly]);
return (
<div className="App">
<p>{A.toString()}</p>
<p>{B.toString()}</p>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
Answered by
dai-shi
Aug 6, 2023
Replies: 1 comment 8 replies
-
Interesting. Does it work without the immer middleware? |
Beta Was this translation helpful? Give feedback.
8 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
No, you should never call actions inside
set
, even without immer. It just happened to work becauseset((state) => ({}))
is a NO-OP, but it's never intended to make side effects within it.