Search code examples
javascriptreactjsreduxreact-reduxredux-thunk

Why getPosts() is called within dispatch parentheses? How dispatch is working here?


export const getPosts = () => dispatch => {
  dispatch(setPostLoading());
  axios
    .get('/api/posts')
    .then(res => {
      dispatch({
        type: GET_POSTS,
        payload: res.data
      })}
    )
    .catch(err =>
      dispatch({
        type: GET_POSTS,
        payload: null
      })
    );
};
export const addLike = id => dispatch => {
  axios
    .post(`/api/posts/like/${id}`)
    .then(res => { 
      dispatch(getPosts())
    })
    .catch(err =>
      dispatch({
        type: GET_ERRORS,
        payload: err.response.data
      })
    );
};

In my project whenever a user click on the like button of a post, addLike function will be called with an id of that post.


Solution

  • If I am understanding your question correctly, you are asking why you have to initially dispatch your action, and then again inside the asynchronous action need to dispatch further actions.

    How dispatch is working here?

    You first dispatch your action "value" to the store by calling the addLike action creator and dispatching the returned "value" to the store.

    dispatch(addLike("123"));
    

    Normally the action creators return an action object, e.g. a Javascript object, with type and payload properties.

    {
      type: "MY_AWESOME_ACTION",
      payload: "This is my awesome payload value!",
    }
    

    However, addLike returns a function when called, e.g. addLike("123") returns:

    (dispatch) => {
      axios
        .post(`/api/posts/like/${id}`) // "/api/posts/like/123"
        .then(res => { 
          dispatch(getPosts());
        })
        .catch(err =>
          dispatch({
            type: GET_ERRORS,
            payload: err.response.data
          });
        );
    };
    

    In other words, the following two code snippets are the same when evaluated.

    dispatch(addLike("123"));
    
    dispatch((dispatch) => {
      axios
        .post("/api/posts/like/123")
        .then(res => { 
          dispatch(getPosts());
        })
        .catch(err =>
          dispatch({
            type: GET_ERRORS,
            payload: err.response.data
          });
        );
    });
    

    Thunks, or asynchronous action creators, return functions instead of objects. You are dispatching a function to the store.

    The Thunk middleware you added to your store when it was created intercepts actions that are functions and waits for them to settle (i.e. resolve or reject). The middleware calls this action function and passes the store's dispatch function (and store.getState and the Thunk middleware extraArgument) to it so further actions can be dispatched as necessary.

    Here's a pretty awesome animation that helps visualize the asynchronous data flow, borrowed from the Redux docs.

    thunk

    Example Thunk:

    const someAsyncAction = (arg) => (dispatch, getState, extraArgument) => {
      ...
    }
    

    Notice there are two "dispatch" function locations, one in the UI and the other the internal dispatch the store/middleware pass.

    Why getPosts() is called within dispatch parentheses?

    Inside your returned addLike action you are using the dispatch function that is passed to it from the Thunk middleware to dispatch the other actions to the store, e.g. dispatch(getPosts());.

    Similarly getPosts is another asynchronous action creator, returning a function that will be passed the dispatch function from the Thunk middleware.

    dispatch(getPosts());
    

    Evaluates to

    dispatch((dispatch) => {
      dispatch(setPostLoading());
      axios
        .get('/api/posts')
        .then(res => {
          dispatch({
            type: GET_POSTS,
            payload: res.data
          })}
        )
        .catch(err =>
          dispatch({
            type: GET_POSTS,
            payload: null
          })
        );
    });