I’ve been banging my head against this, hopefully getting another pair of eyes on it will help.
I’m using Redux + Redux Toolkit in a React Native App, in a pretty simple way. I can tell (through a log statement) that my action is being called and the state is getting set, but my useSelector on the state never updates. I’ve tried it with shallowEqual
as well, but that shouldn’t be needed, since Redux Toolkit uses Immer and the object shouldn’t pass an equality check after updating (most of the other similar issues I researched were due to that)
Here’s my main slice, followed by all the related code. Pardon the code dump, but I want to give a full picture:
export interface Metadata {
title: string
author: string
firstLines: string
id: string
}
type MetadataState = Record<string, Metadata>
export const metadataSlice = createSlice({
name: "metadata",
initialState: {} as MetadataState,
reducers: {
setMetadata: (state: MetadataState, action: PayloadAction<MetadataState>) => {
state = action.payload
console.log("new metadata: ", state)
},
addMetadata: (state: MetadataState, action: PayloadAction<Metadata>) => {
state[action.payload.id] = action.payload
}
}
});
I have an async action to load the metadata from AsyncStorage (like LocalStorage on mobile), as follows:
export function loadMetadata() {
return async (dispatch: AppDispatch, getState: () => RootState) => {
const maybeMetadata = await AsyncStorage.getItem("metadata");
if(maybeMetadata) {
dispatch(metadataSlice.actions.setMetadata(JSON.parse(maybeMetadata)))
return true
} else {
return false
}
}
}
And I dispatch that in my main component as follows:
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(loadMetadata())
}, [])
In another component, I’m trying to access the state simply by doing:
const metadata = useAppSelector(state => state.metadata)
Any idea what’s going on? The state just never seems to update, even though I see my action being called and update the state within it. Is it not being dispatched correctly? I tried directly accessing the state with store.getState()
and the state seems empty, is it somehow just not being set?
I’m honestly pretty lost, any help is appreciated.
2
Answers
The issue had to do with how Immer (which Redux Toolkit leverages for allowing mutable operations) works.
Instead of mutating state, I reassigned it, which messed up the way Immer keep track of draft states. The console.log statement returned the new state, but it didn't work with Immer. Instead, I needed to do this:
And it works fine now. I'm kind of surprised I didn't see this documented (it may be somewhere) or get some sort of warning, but good to know for the future!
An addition to Nathan‘s answer, to avoid linters flooding your code and for proper readability, instead of:
Do it like this:
By so doing, first parameter of the action state, is put to use as it should