Search code examples
javascriptreactjsreduxreact-reduxredux-toolkit

useDispatch doesn't dispatch to the reducer


I'm trying to convert an existing project to configureStore from createStore.

store.js :

export default configureStore({
  reducer: {
    articlesList: loadArticlesReducer
  }
});

home.js :

const articlesList = useSelector((state) => state.articlesList);
const dispatch = useDispatch()

useEffect(() => {
  dispatch(getArticles());
})

articleSlice.js :

const articleSlice = createSlice({
  name: 'articles',
  initialState : [],
  reducers : {
    loadArticlesReducer: (state, action) => {
      console.log("WE NEVER REACH THIS CODE") <=== the problem is here
      state = action.payload;
    }
  }
});

export const { loadArticlesReducer } = articleSlice.actions;

export const getArticles = () => dispatch => {
  fetch("https://....")
    .then(response => response.json())
    .then(data => {
      dispatch(loadArticlesReducer(data))
    })
};

The problem, as stated in the comment, is that getArticles action never dispatches the data to loadArticlesReducer.

What am I missing here?


Solution

  • loadArticlesReducer is an action creator, not a reducer function. I suggest renaming the action creator so its purpose isn't confusing to future readers (including yourself) and actually exporting the reducer function.

    Example:

    const articleSlice = createSlice({
      name: 'articles',
      initialState : [],
      reducers : {
        loadArticlesSuccess: (state, action) => {
          state = action.payload;
        }
      }
    });
    
    export const { loadArticlesSuccess } = articleSlice.actions;
    
    export const getArticles = () => dispatch => {
      fetch("https://....")
        .then(response => response.json())
        .then(data => {
          dispatch(loadArticlesSuccess(data));
        });
    };
    
    export default articleSlice.reducer; // <-- export reducer function
    
    import articlesReducer from '../path/to/articles.slice';
    
    export default configureStore({
      reducer: {
        articlesList: articlesReducer
      }
    });
    

    You may also want to consider converting getArticles to a more idiomatic RTK thunk function using createAsyncThunk. You'd use the slice's extraReducers to handle the fulfilled Promise returned from the Thunk. Example:

    import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
    
    export const getArticles = createAsyncThunk(
      "articles/getArticles",
      () => {
        return fetch("https://....")
          .then(response => response.json());
      }
    );
    
    const articleSlice = createSlice({
      name: 'articles',
      initialState : [],
      extraReducers: builder => {
        builder.addCase(getArticles.fulfilled, (state, action) => {
          state = action.payload;
        });
      },
    });
    
    export default articleSlice.reducer;