Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perform Ajax Fetch in a Redux Reducer?

I'm trying to wrap my head around accessing the state inside Redux actionCreators; instead did the following (performed ajax operation in the reducer). Why do I need to access the state for this — because I want to perform ajax with a CSRF token stored in the state.

Could someone please tell me if the following is considered bad practice/anti-pattern?

export const reducer = (state = {} , action = {}) => {

    case DELETE_COMMENT: {

        // back-end ops
        const formData = new FormData();
        formData.append('csrf' , state.csrfToken);
        fetch('/delete-comment/' + action.commentId , {
            credentials:'include' ,
            headers:new Headers({
                'X-Requested-With':'XMLHttpRequest'
            }) ,
            method:'POST' ,
            body:formData
        })

        // return new state
        return {
            ...state ,
            comments:state.comments.filter(comment => comment.id !== action.commentId)
        };
    }

    default: {
        return state;
    }
}
like image 484
sammysaglam Avatar asked Dec 19 '22 05:12

sammysaglam


2 Answers

From the redux documentation:

The only way to change the state is to emit an action, an object describing what happened. Do not put API calls into reducers. Reducers are just pure functions that take the previous state and an action, and return the next state. Remember to return new state objects, instead of mutating the previous state.

Actions should describe the change. Therefore, the action should contain the data for the new version of the state, or at least specify the transformation that needs to be made. As such, API calls should go into async actions that dispatch action(s) to update the state. Reducers must always be pure, and have no side effects.

Check out async actions for more information.

An example of an async action from the redux examples:

function fetchPosts(subreddit) {
    return (dispatch, getState) => {
        // contains the current state object
        const state = getState();

       // get token
       const token = state.some.token;

        dispatch(requestPosts(subreddit));

        // Perform the API request
        return fetch(`https://www.reddit.com/r/${subreddit}.json`)
            .then(response => response.json())

            // Then dispatch the resulting json/data to the reducer
            .then(json => dispatch(receivePosts(subreddit, json)))
    }
}
like image 50
Wolfie Avatar answered Feb 20 '23 15:02

Wolfie


As per guidelines of redux.

It's very important that the reducer stays pure. Things you should never do inside a reducer:

  • Mutate its arguments;
  • Perform side effects like API calls and routing transitions;
  • Call non-pure functions, e.g. Date.now() or Math.random().

If you are asking whether it is anti-pattern or not then yes it is absolutely.

But if you ask what is the solution.

  1. Here you need to dispatch async-action from your action-creators
  2. Use "redux-thunk" or "redux-saga" for that
  3. You can access the state and create some async action

e.g inside your action-creator ( Just for example )

export function deleteCommment(commentId) {
    return dispatch => {
        return Api.deleteComment(commentId)
            .then( res => {
                dispatch(updateCommentList(res));
            });
    };
}

export function updateCommentList(commentList) {
    return {
        type : UPDATE_COMMENT_LIST,
        commentList
    };
}

Edit: You can access the state -

export function deleteCommment(commentId) {
    return (dispatch, getState) => {
        const state = getState();
        // use some data from state
        return Api.deleteComment(commentId)
            .then( res => {
                dispatch(updateCommentList(res));
            });
    };
}
like image 23
WitVault Avatar answered Feb 20 '23 15:02

WitVault