Reputation: 31
While working on a React Native application with a persistent Redux store (in TypeScript) I ran into the following problem:
I built a Thunk that queries an API, this API sends back a set of question data in a fetch. This fetch works fine, but when trying to save any data to the Redux Store using extraReducers
I get errors.
My code looks like this:
First I call a dispatch on the Thunk in my page:
questionpage.tsx
-stuff-
useEffect(() => {
console.log('dispatching fetch')
dispatch(fetchQuestions())
}, []);
-render stuff-
The actual thunk looks like this and does a fetch to my API:
export const fetchQuestions = createAsyncThunk('questions/fetchquestions', async () =>{
let headers = new Headers();
headers.append('Authorization', 'Basic ' + Base64.btoa(email + ":" + password))
const response = await fetch(api + "/api/questions", {
headers: headers,
method: 'GET'
})
const stuff = await response.json();
console.log(stuff)
return stuff;
})
Then:
questionSlice
extraReducers: (builder) => {
builder.addCase(fetchQuestions.pending, (state, action) => {
state.status = 'loading' // < when I comment this the console.log works
console.log('pending')
}),
builder.addCase(fetchQuestions.fulfilled, (state, action) => {
state.status = 'succeeded'
// Add any fetched posts to the array
state.questions = state.questions.concat(action.payload) // < comment this, console.log works
console.log('fulfilled')
})
builder.addCase(fetchQuestions.rejected, (state, action) => {
state.status = 'failed' // < when I comment this the console.log works
state.error = action.error.message // < when I comment this the console.log works
console.log('rejected')
})
}
As you see above: In my reducer I use the extraReducers
to catch the different results this Thunk can produce. This works, but only when I modify nothing in the state. If I try to save anything to the state I get the following error:
[Unhandled promise rejection: Error: [Immer] Immer only supports setting array indices and the 'length' property]
This error occurs at all times, it occurs when I modify state.status
, which is a String, not an Array, it also occurs when I modify state.error
and state.questions
The last one is an array, but the other two are not.
I looked at a lot of other questions but couldn't find this specific scenario anywhere and am fairly lost on how to proceed.
Update:
My problem stems from Immer
. If I understand the documentation correctly Immer
should allow me to do updates of objects in the Redux state, but I cannot get it to work:
export const questionSlice = createSlice({
name: 'question',
initialState,
reducers: {
save: (state, action) => {
state.questions.push(action.payload) < this action works
}
},
extraReducers: (builder) => {
builder.addCase(fetchQuestions.fulfilled, (state, action) => {
state.questions.push(action.payload) < this action errors
})
Edit 2:
Pointed towards this direction and posting my state here. The app only has one current Slice, the QuestionSlice from earlier, with this state:
interface questionState {
questions: object[],
status: 'idle' | 'loading' | 'succeeded' | 'failed',
error: string | undefined
}
const initialState: questionState = {
questions : [],
status: 'idle',
error: undefined
}
This goes into the RootReducer
const rootReducer = combineReducers({questions: questionReducer})
And then this RootReducer
is used to build the store:
const persistConfig = {
key: 'root',
storage: AsyncStorage
}
const persistedReducer = persistReducer(persistConfig, rootReducer);
const mystore = createStore(
persistedReducer,
applyMiddleware(thunk)
);```
The comment Phry made pointed towards my state being an array of itself, but that doesn't seem to be the case to me. There's also no code I can find that modifies the state to be an array. I'll try and see if I can make a basic version of the project later that others could look at, but it might be a lot of work with my level of experience.
Upvotes: 1
Views: 1866
Reputation: 31
My fix was as follows:
Even though I got an Immer
error and I should be able to do state modification inside the Reducer, if I understand the documentation correctly: this specific part of the documentation, it just didn't work.
I take it there is something with the extraReducers
that gives me that Immer
error. I got my code to work by changing it to be immutable updates.
builder.addCase(fetchQuestions.fulfilled, (state, action) => {
// Add any fetched posts to the array
console.log('fulfilled')
return state = {
...state,
status: 'succeeded',
questions: action.payload
}
})
Upvotes: 2
Reputation: 697
in the reducers you have to copy your state as it is immutable you can't just change a property on it.
Upvotes: 0