Search code examples
reactjsreduxredux-thunkredux-toolkit

Exporting extra reducers from redux toolkit


I made a todo list a while ago as a way to practice react and redux. Now I'm trying to rewrite it with redux toolkit and having some trouble with the action creators.

Here is the old actions creator:

export const changeDescription = (event) => ({
    type: 'DESCRIPTION_CHANGED',
    payload: event.target.value })

export const search = () => {
    return (dispatch, getState) => {
        const description = getState().todo.description
        const search = description ? `&description__regex=/${description}/` : ''
        axios.get(`${URL}?sort=-createdAt${search}`)
            .then(resp => dispatch({ type: 'TODO_SEARCHED', payload: resp.data }))
    } }

export const add = (description) => {
    return dispatch => {
        axios.post(URL, { description })
            .then(() => dispatch(clear()))
            .then(() => dispatch(search()))
    } }

export const markAsDone = (todo) => {
    return dispatch => {
        axios.put(`${URL}/${todo._id}`, { ...todo, done: true })
            .then(() => dispatch(search()))
    } }

export const markAsPending = (todo) => {
    return dispatch => {
        axios.put(`${URL}/${todo._id}`, { ...todo, done: false })
            .then(() => dispatch(search()))
    } }

export const remove = (todo) => {
    return dispatch => {
        axios.delete(`${URL}/${todo._id}`)
        .then(() => dispatch(search()))
    } }

export const clear = () => {
    return [{ type: 'TODO_CLEAR' }, search()] }

Now this is the one that I'm working on, I'm trying to replicate the actions of the old one but using redux toolkit:

export const fetchTodos = createAsyncThunk('fetchTodos', async (thunkAPI) => {
    const description = thunkAPI.getState().todo.description
    const search = description ? `&description__regex=/${description}/` : ''
    const response = await axios.get(`${URL}?sort=-createdAt${search}`)
    return response.data
})

export const addTodos = createAsyncThunk('fetchTodos', async (thunkAPI) => {
    const description =  thunkAPI.getState().todo.description
    const response = await axios.post(URL, {description})
    return response.data
})

export const todoReducer = createSlice({
    name: 'counter',
    initialState: {
        description: '',
        list: []
    },
    reducers: {
        descriptionChanged(state, action) {
            return {...state, dedescription: action.payload}
        },
        descriptionCleared(state, action) {
            return {...state, dedescription: ''}
        },

    },
    extraReducers: builder => {
        builder
         .addCase(fetchTodos.fulfilled, (state, action) => {
             const todo = action.payload
             return {...state, list: action.payload}
         })
         .addCase(addTodos.fulfilled, (state, action) => {
            let newList = state.list
            newList.push(action.payload)
            return {...state, list: newList}
        })
    }
})

The thing is, I can't find anywhere how to export my extra reducers so I can use them. Haven't found anything in the docs. Can someone help?


Solution

  • extraReducers

    Calling createSlice creates a slice object with properties reducers and actions based on your arguments. The difference between reducers and extraReducers is that only the reducers property generates matching action creators. But both will add the necessary functionality to the reducer.

    You have correctly included your thunk reducers in the extraReducers property because you don't need to generate action creators for these, since you'll use your thunk action creator.

    You can just export todoReducer.reducer (personaly I would call it todoSlice). The reducer function that is created includes both the reducers and the extra reducers.

    Edit: Actions vs. Reducers

    It seems that you are confused by some of the terminology here. The slice object created by createSlice (your todoReducer variable) is an object which contains both a reducer and actions.

    The reducer is a single function which takes the previous state and an action and returns the next state. The only place in your app when you use the reducer is to create the store (by calling createStore or configureStore).

    An action in redux are the things that you dispatch. You will use these in your components. In your code there are four action creator functions: two which you created with createAsyncThunk and two which were created by createSlice. Those two will be in the actions object todoReducer.actions.

    Exporting Individually

    You can export each of your action creators individually and import them like:

    import {fetchTodos, descriptionChanged} from "./path/file";
    

    Your fetchTodos and addTodos are already exported. The other two you can destructure and export like this:

    export const {descriptionChanged, descriptionCleared} = todoReducer.actions;
    

    You would call them in your components like:

    dispatch(fetchTodos())
    

    Exporting Together

    You might instead choose to export a single object with all of your actions. In order to do that you would combine your thunks with the slice action creators.

    export const todoActions = {
      ...todoReducer.actions,
      fetchTodos,
      addTodos
    }
    

    You would import like this:

    import {todoActions} from "./path/file";
    

    And call like this:

    dispatch(todoActions.fetchTodos())