41

I have seen conflicting (or just confusing, to me) answers in other questions here regarding whether using getState within an action is acceptable, or not, and I have seen quite a few times it being called an anti-pattern. For me, it seems to work great but what is the best practice for doing this if we are not to use getState?

I am using getState within a thunk to filter through an array of users that is currently connected to some mock data and being pulled into the state of the application.

Here is the code for my action:

export const accountLogInSuccess = user => ({
    type: types.ACCOUNT_LOG_IN_SUCCESS,
    user,
});

export const accountLogOutSuccess = () => ({
    type: types.ACCOUNT_LOG_OUT_SUCCESS,
});

export const accountCheckSuccess = () => ({
    type: types.ACCOUNT_CHECK_SUCCESS,
});

export const accountCheck = () => (
    (dispatch, getState) => {
        dispatch(ajaxCallBegin());
        return apiAccount.accountCheck().then((account) => {
            if (account) {
                const user = findByUID(getState().users, account.uid);
                dispatch(accountLogInSuccess(user));
                toastr.success(`Welcome ${user.nameFirst}!`);
            } else {
                dispatch(accountLogOutSuccess());
            }
            dispatch(accountCheckSuccess());
        }).catch((error) => {
            dispatch(ajaxCallError(error));
            toastr.error(error.message);
            throw (error);
        });
    }
);

And my reducer:

export default function reducerAccount(state = initial.account, action) {
    switch (action.type) {
    case types.ACCOUNT_LOG_IN_SUCCESS:
        return Object.assign({}, state, action.user, {
            authenticated: true,
        });
    case types.ACCOUNT_LOG_OUT_SUCCESS:
        return Object.assign({}, {
            authenticated: false,
        });
    case types.ACCOUNT_CHECK_SUCCESS:
        return Object.assign({}, state, {
            initialized: true,
        });
    default:
        return state;
    }
}

The initial state of account used in my reducer is just:

account: {
    initialized: false,
    authenticated: false,
},

The accountCheck action passes the user (found using getState and the findByUID function) into accountLogInSuccess where the reducer adds its values to the current state of account via Object.assign.

Preferring to not have to get the user at the root of my application and then passing it down via props, what is the best practice to accomplish this within Redux and having the user data be available in state? Again, using getState within the thunk works great for me thus far, but is there a better solution to this that is not considered an anti-pattern?

A Mehmeto
  • 1,594
  • 3
  • 22
  • 37
spunge
  • 2,827
  • 3
  • 14
  • 12

1 Answers1

46

I wrote an extended blog post called Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability, which addresses this topic in detail. In it, I respond to several critiques of thunks and use of getState (including Dan Abramov's comments in Accessing Redux state in an action creator?). In fact, my post was specifically inspired by questions like yours.

As a TL;DR of my post: I believe that thunks are a completely viable tool for use in Redux applications, and encourage their use. While there are some valid concerns to be aware of when using thunks and sagas, and using getState/select inside of them, these concerns should not scare you away from using thunks.

Community
  • 1
  • 1
markerikson
  • 63,178
  • 10
  • 141
  • 157
  • 24
    so is using getState in middleware it bad practice? This answer seems to answer whether using thunks is good practice. – JBaczuk May 20 '19 at 15:00
  • 3
    No, it's not a bad practice, as my blog post says. – markerikson May 20 '19 at 16:07
  • 37
    Cool, it'd be nice to put that in the answer, so it's not a link-only answer. – JBaczuk May 20 '19 at 16:08
  • 5
    this is a bad answer as the content exists in ephemeral links. – FlavorScape Feb 14 '20 at 19:23
  • 1
    @JCraine : not sure what your complaint is here. For the record, while Dan created Redux, I have been the primary maintainer since mid-2016. Today [we still teach thunks as the default approach for async logic](https://redux.js.org/tutorials/essentials/part-5-async-logic), and `getState` is a standard part of the thunk API. If it wasn't meant to be used it wouldn't be in there :) – markerikson Mar 26 '22 at 03:06
  • @JBaczuk Neither OPs question nor markerikson's answer are addressing middlewares. How is this relevant? – timotgl Feb 02 '23 at 09:50
  • Redux's middleware API was specifically designed so that middleware have access to `getState` and `dispatch`. Like I said a year ago, we wouldn't have designed middleware to get those functions if you weren't supposed to use them :) – markerikson Feb 04 '23 at 01:25