Practical tips for adding state using React and Redux

Photo by Marten Newhall on Unsplash

Lost track of your state? Happens to the best of us! Enter Redux, a state management tool. I had to speed learn Redux and React for a project, and this article is designed to help you quickly add a piece of state to your app with actions and reducers. Hopefully I can help demystify state, actions, and reducers when using Redux state management.

For reference I use a React/Redux front end and a rails API backend.

Setup

I said practical — so let’s define what we’re trying to do. We’re going to add and manage a piece of state called profile. You can get the current profile via the endpoint /profiles. For those new to frontend/backend integration, you will also need to set up the route and the controller action in your rails app served as json.

For this example assume your profile information is returned via json with user_name, avatar, id as the keys. Something like this:

{
"id": 1,
"avatar": {
Some url avatar data
},
"user_name": Samantha Jones
}

Redux

Redux manages your state. Your state is the information you want to access (the profile information listed above). Redux has three parts — the state (aka the information), actions, and reducers. Don’t let the fancy names scare ya, it’s just javascript.

  1. State: The current state of data in your application. A great example — if a user is logged in and who the currently logged in user is.
  2. Actions: How you CHANGE the state. For example, when your user logs out, you need to change the loggedin state to false. Well how do you do that?
  3. Reducers: No idea why they have this fancy name. Because they “reduce” the state maybe? Anyway, reducers are what changes your state based on your actions.

This will all make SO MUCH more sense with an example. Okay, so you want to add the current profile to state, get the profile data, and figure out if the user is logged in.

Step 1: Action. In actions/profileActions.js

Note: I’m importing authHeadesr using redux-token-auth and also importing axios to make my requests.

export const getProfile = (profileId) => {
return (dispatch) => {
dispatch({ type: GET_PROFILE_STARTED });
axios.get(`mygreatapp/api/v1/profiles/${profileId}`, authHeaders())
.then(
response => {
dispatch({ type: GET_PROFILE_SUCCESS, payload: response });
},
error => {
dispatch({ type: GET_PROFILE_ERROR, payload: error.response });
},
);
};
};

You’re dispatching an action, or doing something to change the state. The type is a constant. So — you get the endpoint. If successful your endpoint will return user_name, avatar, and id in this example. That is your payload: response above. Remember, you can always type the url directly in your browser and add .json to see your json output.

Step 2: Reducer. In reducers/profile.js

const initialState = {
data: {},
errors: [],
isLoggedIn: false
};
export default (state = initialState, action) => {
switch (action.type) {
case GET_PROFILE_STARTED:
return {...initialState, isLoggedIn: false};
case GET_PROFILE_SUCCESS:
return {
...initialState,
isLoggedIn: true,
data: action.payload.data
};
case GET_PROFILE_ERROR:
return {...initialState, isLoggedIn: false, errors: action.payload.data.error};
default:
return state;
}
}

The first thing you need to do is set an initial state. Then you have a switch statement which changes your state based on the type. If you refer back to the actions, you can see we immediately dispatch a type of GET_PROFILE_STARTED. Cross reference that will the reducer above, and you can see GET_PROFILE_STARTED returns the initial state with no change. Makes sense, we haven’t changed anything yet! Back to the action — next we send a get request. If successful, the request will return a payload of json data. Our data is determined by the response to our get request, and in this example it is user_id, id, and avatar. Now that our get request was successful, we come back to our reducer. You can see the case of GET_PROFILE_SUCCESS changes the isLoggedIn state to true and takes the user data user_id, id, and avatar and puts them in the data portion of state.

3. Accessing our state

Now we have a piece of state called profile that has data , errors, and isLoggedIn. So if we want to get a profile user_name we can access it with state.profile.data.user_id in our react components

4. Tips on view state: I learned this great tip from Wes Bos on viewing state. I work on a large application with many developers. Sometimes I don’t know if a piece of information is in state already or not. When in doubt:

  • Go to the React dev tools
  • Search for Provider and select it as shown below
  • Go to your console and type $r.store.getState() . This will list all of the current state!

5. Add your reducer to index.js. In /reducers/index.js you need to import your reducer and define your rootReducer.

import profile from './profile';
const rootReducer = combineReducers({
profile
});
export default rootReducer;

Ta Da! Is it me, or does that seem like a lot of work to add one piece of state? This doesn’t even touch on containers and actions! But the in place render is pretty awesome….so I guess it’s worth it!