Search code examples
reactjsreact-reduxredux-toolkit

How to store action.payload in redux-toolkit correctly?


I am using redux toolkit to fetch users from an API and save the user's state in the store.

  userStore: {
    users: [],
    loading: true
  }
}

This shows up in redux-toolkit dev tools in firefox.

I was expecting it to be.

  userStore: {
    users: Array[..], //an array of 10 users
    loading: false
  }
}

I have configured the store in index.js

const store = configureStore({
    reducer: {
        userStore: usersReducer,
    },
});

export default store;

userSlice.js


const initialState = {
    users: [],
    loading: false,
};

export const userSlice = createSlice({
    name: 'users',
    initialState,
    reducers: {
        receivedUserList: (state, action) => {
            state.users = action.payload;
        },
        toggleLoadingUsers: (state) => {
            state.loading = !state.loading;
        },
    },
});

export const { receivedUserList, toggleLoadingUsers } = userSlice.actions;

export default userSlice.reducer;

User actions.js


export const fetchUsers = (data) => async (dispatch) => {
    try {
        dispatch(toggleLoadingUsers());
        const response = await userAPI.get('/users');
        dispatch(receivedUserList(response.data));
        dispatch(toggleLoadingUsers());
    } catch (error) {
        console.log(error);
    }
};

I am using fetchusers in my App.js like this.

    const dispatch = useDispatch();
    const { users, loading } = useSelector((state) => state.userStore);
    useEffect(() => {
        console.log('something');
        console.log(loading);
        console.log(users);
        dispatch(fetchUsers());
    }, []);

This is not updating the state correctly. How do I set the state after fetching users? I have passed the store in ./store/index.js in src/index.js

import store from './store/index';
import { Provider } from 'react-redux';

ReactDOM.render(
    <React.StrictMode>
        <Provider store={store}>
            <App />
        </Provider>
    </React.StrictMode>,
    document.getElementById('root')
);

Solution

  • The idea behind Redux's Toolkit is to remove boilerplate code for actions, types, and reducers and unified them into a single slice.

    Let's refactor the user slice code as below.

    
    import { createAsyncThunk } from "@reduxjs/toolkit";
    
    export const fetchUsers = createAsyncThunk("users/fetchUsers", async (data) => {
      const response = await userAPI.get("/users");
    
      return response.data;
    });
    
    const initialState = {
      users: [],
      loading: false,
    };
    
    export const userSlice = createSlice({
      name: "users",
      initialState,
      reducers: {
        receivedUserList: (state, action) => {
          state.users = action.payload;
        },
        toggleLoadingUsers: (state) => {
          state.loading = !state.loading;
        },
      },
    
      extraReducers: {
        [fetchUsers.pending]: (state, action) => {
          state.loading = true;
        },
        [fetchUsers.fulfilled]: (state, action) => {
          state.loading = false;
          state.users = [...state.users, ...action.payload];
        },
        [fetchUsers.rejected]: (state, action) => {
          state.loading = true;
          state.error = action.error;
        },
      },
    });
    
    export const { receivedUserList, toggleLoadingUsers } = userSlice.actions;
    
    export default userSlice.reducer;
    
    
    

    Then import fetchUsers from from userSlice

    //top-level import 
    import {fetchUsers} from "../userSlice"
    
    
        const dispatch = useDispatch();
        const { users, loading } = useSelector((state) => state.userStore);
        useEffect(() => {
            console.log('something');
            console.log(loading);
            console.log(users);
            dispatch(fetchUsers());
        }, []);