Kontantin
Kontantin

Reputation: 67

Redux Toolkit Problem, getting a Cannot destructure property as it is undefined

Hey Guys im trying to play around with the redux toolkit and i have a wierd issue i get :

Cannot destructure property 'currentRequestId' of 'getState(...).toys' as it is undefined

vs code tells me that the await before the query is: 'await' has no effect on the type of this expression

The currentRequestId really doesnt show in the store, even tho i set it in the code

i copied the example from their site and modified it, perhaps my axios calls kills it? i can see my call isnt even being executed for some reason.

i dont know how to implement a UserApi like the showed in their example so im using a regular axios call.

My Code:

import React from 'react'
import { createAsyncThunk, createSlice, unwrapResult  } from '@reduxjs/toolkit'
import axios from 'axios'
import { useDispatch,useSelector } from 'react-redux'

 export const query = createAsyncThunk(
  '/api/toys',
  async (criteria, { getState, requestId }) => {
    const { currentRequestId, loading } = getState().toys
    if (loading !== 'pending' || requestId !== currentRequestId) {
      return
    }

    const response = await axios.get('http://localhost:3030/api/toys')
    return response.data
  }
)

export const ToySlice = createSlice({
  name: 'toys',
  initialState: {
    entities: [],
    loading: 'idle',
    currentRequestId: undefined,
    error: null
  },
  reducers: {},
  extraReducers: {
    [query.pending]: (state, action) => {
      if (state.loading === 'idle') {
        state.loading = 'pending'
        state.currentRequestId = action.meta.requestId
      }
    },
    [query.fulfilled]: (state, action) => {
      const { requestId } = action.meta
      if (state.loading === 'pending' && state.currentRequestId === requestId) {
        state.loading = 'idle'
        state.entities.push(action.payload)
        state.currentRequestId = undefined
      }
    },
    [query.rejected]: (state, action) => {
      const { requestId } = action.meta
      if (state.loading === 'pending' && state.currentRequestId === requestId) {
        state.loading = 'idle'
        state.error = action.error
        state.currentRequestId = undefined
      }
    }
  }
})

export const Test = () => {
  const { toys, loading, error } = useSelector(state => state.test)
  const dispatch = useDispatch()


  const fetchAllToys = async userId => {
    try {
      const resultAction = await dispatch(query())
      const toys = unwrapResult(resultAction)
      console.log("Test -> user", toys)
      return toys
    } catch (err) {
    console.log("UsersComponent -> err", err)

    }
  }
return(<div>
    Hello
    <button onClick={()=>fetchAllToys()}>Fetch</button>
</div>)
  // render UI here
}


export const selectCount = state => state.counter.value;

export default ToySlice.reducer;

The Store:

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import Test from '../features/Test/test'
export default configureStore({
  reducer: {
    counter: counterReducer,
    test:Test
  },
});

Link To Example: https://jsfiddle.net/3xq7Lng2/

The Docs:

https://redux-starter-kit.js.org/api/createAsyncThunk

Upvotes: 2

Views: 8437

Answers (1)

markerikson
markerikson

Reputation: 67469

Your thunk is trying to access getState().toys.currentRequestId.

However you don't have a state.toys key, because you're not passing in a field named toys to configureStore. You're defining state.counter and state.Test, but there's no state.toys, so it will be undefined.

I don't know what the file name that has that ToySlice in it. Based on the imports, I'm assuming that it's actually the file named features/Test/test.js.

The immediate fix would be to change the store setup code to:

import toysReducer from '../features/Test/test';

export default configureStore({
  reducer: {
    counter: counterReducer,
    toys: toyReducer
  },
});

That will result in state.toys existing.

You'll also need to update your component, which is currently doing useSelector(state => state.test), and change it to state => state.toys.

From there, I'd recommend changing the folder and filename to features/toys/toysSlice.js and updating the import statements accordingly.

Upvotes: 3

Related Questions