Search code examples
arraysjsonreactjsreduxredux-thunk

Redux returning an empty arrays in store - Reducer not working correctly?


I have recently put Redux into my app for the first time and thought I had it working but it seems to be returning empty arrays. I have checked my Postman get posts and it is working fine on the backend. Should my store be returning values if the arrays are empty like below?

What is likely the issue? I have a async Thunk action creator for it and a create slice Reducer that I thought were working ok.

If my index combineReducers that are createSlice are all appearing in white does this mean they aren't working correctly? The auth and message ones are in yellow and my login works correctly however I didn't use createSlice for them.

Update: I think this is to do with the syntax of my extraReducers "state: actionpayload.field". There is no error message flagging but i'm not sure it's doing what it is meant to do.

Or could this be to do with the fact that I have a combineReducer for my store and passing through reducers that are createSlice? (Should be configureStore for Redux toolkit) my Auth and messages work ok but they aren't Redux. Does configureStore allow both createSlice and normal switch statements at the same time?

index.js

export default combineReducers({

    // combine the reducers
                user,
                fields,
                article,
                diveLog,
                marineList,
                diveSchool,
                diveSpot,
                admin,
                auth,
                message
});

reducer

const fieldsSlice = createSlice({
    name: 'diveLogFields',
    initialState: {
        current: [],
        region: [],
        diveType: [],
        visibility: [],
        diveSpot: [],
    },
    reducers: {},
    extraReducers: {
        // picks up the success action from the thunk
        [requireFieldData.fulfilled.type]: (state, action) => {
            // set the property based on the field property in the action
            (state: action.payload.field); (state: action.payload.items)
        }
    }
})
export default fieldsSlice.reducer;

action

export const requireFieldData = createAsyncThunk(
    'fields/requireData', // action name
    // action expects to be called with the name of the field
    async (fields) => {
        // you need to define a function to fetch the data by field name
        const response = await diveLogFields(fields);
        const { data } = response;
        // what we return will be the action payload
        return {
            fields,
            items: data.data
        };
    },
    // only fetch when needed
    {
        condition: (fields, {getState}) => {
            const {field} = getState();
            // check if there is already data by looking at the array length
            if ( field[fields].length > 0 ) {
                // return false to cancel execution
                return false;
            }
        }
    }
)

Update

I am still getting the below error message when I try to render my page. I had to go into my store and add the compose Redux import as well.

Where does this message suggest the problem is?

enter image description here

enter image description here


Solution

  • I see few minor issues in your code, So, below are fixes and the explanations:

    Slice:

    const fieldsSlice = createSlice({
      name: 'diveLogFields',
      initialState: {
        current: [],
        region: [],
        diveType: [],
        visibility: [],
        diveSpot: [],
        fields: [], // Add initial fields array (if you want to store it)
        items: [], // Add initial items array (if you want to store it)
      },
      reducers: {},
      extraReducers: {
        [requireFieldData.fulfilled.type]: (state, action) => {
          state.fields = action.payload.fields
          state.items = action.payload.items
        },
      },
    })
    export default fieldsSlice.reducer
    

    In the above code, you can use state.fields = ... and state.items = ... to set data in state. It looks like we are directly mutating the state, but we are not because ...

    Redux Toolkit allows us to write "mutating" logic in reducers. It doesn't actually mutate the state because it uses the Immer library, which detects changes to a "draft state" and produces a brand new immutable state based off those changes

    AsyncThunkAction:

    // Assumption, fake async call
    function diveLogFields(fields) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({ data: { data: [1, 2, 3] }, fields })
        }, 2000)
      })
    }
    
    export const requireFieldData = createAsyncThunk(
      'diveLogFields/requireFieldData',
      async (fields) => {
        const response = await diveLogFields(fields)
        const { data } = response
        return {
          fields,
          items: data.data,
        }
      },
      {
        condition: (fields, { getState, extra }) => {
          const { fields: fieldsState } = getState() // getState() returns your Redux State
          if (fieldsState.fields.length > 0) {
            return false // cancel the action if fields has some data
          }
        },
      }
    )
    

    Here is an example to dispatch the async action:

    function MyComponent() {
      const dispatch = useDispatch()
    
      // for example, make async call below hook
      useEffect(() => {
        dispatch(requireFieldData([4, 5, 6]))
      }, [dispatch])
    
      return (
        <>i am MyComponent</>
      )
    }
    

    Now, here is how the state looks like after dispatching the action:

    redux state