Reading Redux? Easy!

Limor Mekaiten
WeWork
Published in
6 min readMay 14, 2018

--

When I first read the Redux docs and went over the great tutorial, I understood the basic idea and how to “get things to work with the Redux store”…for just one component. Then, when trying to read an existing code of a big react application that uses Redux, I understood that I don’t really understand.

So, in this post, I’ll try to give you a very simple walk-through of the very basic things you need to know about Redux flow, and what to look for first when reading existing code.

Note: this post isn’t a first time introduction to Redux. If you need a Redux 101 I recommend on the links above.

Let’s get started!

First, let’s refresh some of the Redux terms so we could have some common language:

  • Everything that changes in your app, including the data and UI state is contained in a single object — called the application state (simply the state, not to mix with each components’ own state).
  • The state lives in the Redux store.
  • There is usually only one global state per application (unlike other Flux implementations, like Reflux).
  • The state is read-only (immutable, hence, very reliable and we avoid sync issues). Note that this read-only property is not by nature but by practice and implementation — it should be enforced manually by the developers.
  • Any time you want to change the state (or, more accurately — to replace it with a new state), you need to dispatch an action.
  • This action gets into the relevant Reducer, which is basically a pure function that replaces the relevant Redux state node with a new state, based on the dispatched action.
  • Each component in the Redux circus is “listening” to this state, and updating the UI/behavior accordingly:
Redux flow

Great, so now that we’re on the same page, we can move on into practice!

Note that in the previous explanation of Redux ideas, I didn’t mention any React. That’s because, believe it or not, Redux can be used without React!
But, since we are talking about React in this post, let’s see how to use it in our React app.

react-redux library gives you an easy way to integrate your React code with Redux.
I want to take a slightly different approach, and instead of building a React component with Redux from scratch, let’s look at existing code that uses Redux, and see how it can be read easily with just a few new terms to our vocabulary:

  1. Look for the component declaration structure — if it uses Redux’ connect, it means that we’re probably talking Redux!
    The method connect binds our component (in this case, LanguagesComponent) with the Redux store, together with two optional function arguments: mapStateToProps and mapDispatchToProps. You could also think of it as a “Redux wrapping” of your component*.
export default connect(mapStateToProps, mapDispatchToProps)(
LanguagesComponent
);

[*actually, it creates a new component and injects some props into it, but let’s keep it simple for this article’s purpose.]

2. mapStateToProps(state, [ownProps])is a function (that you need to implement!) that gives you a neat way to use parts of your Redux state’s data as if someone passed them to your component via props.

So, for example, if your Redux state looks like this:

languages: {
names: [
"English",
"French",
"Spanish",
"Swedish",
],
phrases: {
"English": [
"Hello"
],
"French": [
"Bonjour"
],
"Spanish": [
"Hola"
],
"Swedish": [
"Halla"
],
},
}

and you want to simply display the languagesnames values in your component, you can implement mapStateToProps in the following way:

const mapStateToProps = (state, props) => ({
languagesNames: state.languages.names,
});

and then you can use it in your component by simply referring to this.props.languagesNames from within your component.
In a similar way, you could get all the French phrases by accessing this.props.frenchPhrases if you wish to:

const mapStateToProps = (state, props) => ({
languagesNames: state.languages.names || [],
frenchPhrases: state.languages.phrases['French'],
});

3. mapDispatchToProps(dispatch, [ownProps]) is a function that gives you a way to “inform” your component of its available actions (or action creators) to dispatch, and, similar to mapStateToProps, put them on the component’s props. Here’s an example of such an implementation:

import { addLanguage } from 'redux/modules/languages';const mapDispatchToProps = (dispatch, props) => ({
onLanguageAdded: language => {
dispatch(addLanguage(language));
}
});

Here, we “imported” our action creator addLanguage to be dispatched via our props by callingthis.props.onLanguageAdded. So, for example, if the user clicked on the button in our component to add a new language (say, Hebrew), we can call this.props.onLanguageAdded('Hebrew'), and it will update our Redux state with this new language.

So, with these 3 simple functions, you can get a basic understanding of how your component interacts with the Redux store:

The components determine what to present on the screen based on data from the Redux global state (via mapStateToProps), and can affect the state by dispatching an action (via mapDispatchToProps), which will, in turn, cause a UI change (since the component is “listening” to the current state.

Time to complete the picture!

What happens then when you dispatch your desired action?

Generally speaking, each action is an object composed of an action type and a payload. In our following example, we see an action creator that lets you create such an action (a function that creates an action):

export const ADD_LANGUAGE = 'ADD_LANGUAGE'; //the action typeexport const addLanguage = (languageName) => ({
type: ADD_LANGUAGE,
payload: { languageName },
});

The type parameter will help us determine what kind of action do we want to dispatch, and the payload parameter will contain any needed extra data for that action.

Now, as promised, our dispatched action will go through our reducer in order to create a new state for our app, based on this action.

Our reducer (which will reside in our redux/modules/languagesReducer.js, usually together with its relevant actions), will look something like that:

handleActions is simply a switch-case between action types and their relevant reducers (it comes from the popular redux-actions library). So, in our ADD_LANGUAGE example, the relevant reducer is a function that gets a state and an action, and returns a new state with the new language (whose name is on that action) to the languages state tree.
(Confused by the {[foo]: bar} syntax? You might want to read here.)

As a default, our reducer returns the initialState, which defines the initial structure of our specific state sub-tree:

// Initial State
export const initialState = {
names: [],
phrases: {},
};

To recap everything so far..

We used our mapDispatchToProps to dispatch an action to our reducer, which in turn created a new state, that in turn affects our lovely component that automatically listens to the data from the state using mapStateToProps. Phew!

One last thing..!

You might have asked yourself, how do our components even know about the Redux store? For this with have a special component called Provider (not to be confused with React’s Context API Provider) to magically make the store available to all the components in our app, without having to pass them explicitly. It injects your store into all your app components:

import { Provider } from 'react-redux'
import store from './store/configureStore';
import App from './components/App'

render(
<Provider store={store}>
<App />
</Provider>,
);

What’s in that store variable? Among other things, it will have your root reducer, and your initial state!

Taking things to the next level

When dealing with a real app, you’ll probably want to split your actions handling into separate reducer functions, each managing independent parts of the Redux state, to keep your app both readable and keep some separation of concerns also in how you handle your data. For instance, you’d want one reducer to handle the languages, one to keep your users’ info, maybe one to keep users’ ‘likes’, and so on. How can our app achieve that?

For this, we have a helper function in the Redux lib called combineReducers, that given an object whose values are different reducers, gives you a single reducer function that contains all of the reducers’ logic:

import languagesReducer from './languagesReducer';
import usersReducer from './usersReducer';

rootReducer = combineReducers({languages: languagesReducer, users: usersReducer});
// This would produce the following state object
{
languages: {
// ... languages
},
users: {
// ... users
}
}

Now you can understand why in our mapStateToProps we could access our languages data by state.languages.

So we did the Redux hokey pokey, is that what it’s all about?

Yes and no!

This was the basic flow of Redux. You might still see more advanced stuff like Selectors, Async calls in your actions, Middleware, and more fun.

My humble advice? Don’t freak out!

Start getting a feeling of how Redux works with a simple component, and then you can move on to learning the more advanced concepts. I highly recommend using the great Redux DevTools extension to actually get a feeling of “what’s going on in the Redux state”.

Good luck!

Enjoyed reading this post? Don’t hesitate to share and spread the love!

--

--