how do I migrate from redux to redux toolkit

Viewed 2050

I managed to write reducer using createSlice but the action seems to be confusing.

My old reducer :

function listPeopleReducer(state = {
    getPeople:{}
}, action){
    switch (action.type) {
        case D.LIST_PEOPLE: {
            return {
                ...state
                , getPeople:action.payload
            }
        }
            default:{}
    }
    return state
}

By using createSlice from the redux toolkit, I migrated the reducer to this,

const listPeopleReducer = createSlice({
    initialState:{getPeople:{}},
    name:"listPeople",
    reducers:{
        listPeople(state,action){
            return {
                ...state,
                getPeople : action.payload
            } 
        }
    }
})

My old action, makes an api call inside it, with the help of a helper function makeApiRequest (which takes in parameters and returns the response of the api),

export function listPeople(config: any) {
    return function (dispatch: any) {
        makeApiRequest(config)
        .then((resp) => {
            dispatch({
                type :  D.LIST_PEOPLE,
                payload : resp.data
            })
        })
        .catch((error) => {
            dispatch({
                type :  D.LIST_PEOPLE,
                payload : error
            })
        })
    }
} 

With reduxtool kit, we could do something like,

const listPeople = listPeopleReducer.actions.listPeople;

But, how will I write my custom action that contains the helper function makeApiRequest ? i.e The old Action should be migrated to reduxtoolkit type.

2 Answers

It's definitely tricky when migrating, since there are some major conceptual changes that you must eventually wrap your head around. I had to do it a couple of times before it clicked.

First, when you are creating const listPeopleReducer with createSlice(), that is not actually what you are creating. A slice is a higher level object that can generate action creators and action types for you, and allows you to export reducers and actions FROM it.

Here are the changes I would make to your code:

const peopleSlice = createSlice({
    initialState:{getPeople:{}},
    name:"people",
    reducers:{
        listPeople(state,action){
            // uses immer under the hood so you can
            // safely mutate state here
            state.getPeople = action.payload
        }
    },
    extraReducers:
        // each thunk you create with `createAsyncThunk()` will
        // automatically have: pending/fulfilled/rejected action types
        // and you can listen for them here
        builder => 
            builder.addCase(listPeople.pending, (state,action) => {
                // e.g. state.isFetching = true
            })
            builder.addCase(listPeople.fulfilled, (state,action) => {
                // e.g. state.isFetching = false
                // result will be in action.payload
            })
            builder.addCase(listPeople.rejected, (state,action) => {
                // e.g. state.isFetching = false
                // error will be in action.payload
            })
    }
})

Then, outside of your slice definition, you can create actions by using createAsyncThunk(), and do like:

export const listPeople = createAsyncThunk(
    `people/list`,
    async (config, thunkAPI) => {
        try {
            return makeApiRequest(config)
        } catch(error) {
            return thunkAPI.rejectWithError(error)
            // thunkAPI has access to state and includes
            // helper functions like this one
        }
    }
} 
Related