Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 421
James Gee Avatar asked Mar 16 '21 15:03

James Gee


People also ask

How do you empty an array in Redux?

@aw04 but then it will just add '[]' as a new element in the array. If you want the next state to be an empty array, then just return an empty array. return [] would make the state equal to your default state.

Should I keep all component's state in Redux store?

Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state. Using local component state is fine.

How do I retrieve data from store Redux?

It's simple to get access to the store inside a React component – no need to pass the store as a prop or import it, just use the connect function from React Redux, and supply a mapStateToProps function that pulls out the data you need. Then, inside the component, you can pass that data to a function that needs it.

Where does Redux actually store data?

It's stored in browser's memory.

What are Redux reducers used for?

Applying redux reducers to arrays The redux pattern provides a simple (and strict) pattern for managing state; a single store holds the state while a reducer applies actions, evolving the application state.

What is a redux pattern?

The redux pattern provides a simple (and strict) pattern for managing state; a single store holds the state while a reducer applies actions, evolving the application state.

Why is my store not having a valid reducer?

More... so now we have an issue which is: Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers. . also, this type of error can be if we mistake the file spelling or we forgot to import the reducer file into the store file or if made mistake in including the file path.

How do I render multiple instances of a counter in Redux?

In order to render multiple counter instances, this component has to be stateless. With the way the redux connectors work, this is a trivial change to make. The component render function remains the same, but the state and dispatch property mappings are removed: Finally, a new CounterCollection component is added that renders the application state:


2 Answers

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

like image 131
Ajeet Shah Avatar answered Oct 19 '22 17:10

Ajeet Shah


Got it working. I had to change my store.js file to the below as it was previously set-up differently. I also used the code changes suggested in my replies.

const store = createStore(
    rootReducer,
    compose(
        applyMiddleware(thunk),
        window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    )
);

export default store;
like image 2
James Gee Avatar answered Oct 19 '22 15:10

James Gee