Introduction to Redux Part 2 (with ReactJS)

by SlideFactory

“In this blog post we’re going to explore using Redux inside of the popular JavaScript framework, ReactJS”

Overview

In this blog post we’re going to explore using Redux inside of a JavaScript (JS) framework. We covered the basics of Redux with vanilla JS inside our Part 1 tutorial. But in this post, let’s see how Redux works with one of its most popular partnering frameworks, ReactJS.

If you are curious about the challenge solution from Part 1, you can find it at the bottom of this post.

ReactJS Basics

If you are not familiar with the basic concepts of ReactJS, I’ll quickly cover them below. The ReactJS documentation has some excellent tutorials as well to get you off the ground. Below we will cover a little bit about React Components and how we can run into problems that Redux can help us solve.

Components

In React, components hold your code. These components can be simple functions such as:

const Post = (props) => {
  return (
    <div>
      <h1>{props.title}</h1>
      <div>{props.content}</div>
    </div>
  );
}

Or extend React’s builtin Component to give access to component state management and React lifecycles:

class Post extends React.Component {
  constructor(props) {
    super(props);
    
    this.state = {
      local: 'I am a local state var!',
    };
  }

  // This lifecycle function runs when the component
  // is added to the DOM
  componentDidMount() {
    console.log("mounted");
  }
  
  render() {
    return (
      <div>
        <h1>{this.props.title}</h1>
        <div>{this.props.content}</div>
      </div>
    );
  }
}

It’s important to note that a component can manage its own state. This is separate from a global state manager such as Redux, which multiple components can look at.

React JSX Syntax

Each of the Post components above is used exactly the same way, with React’s JSX syntax, which allows us to write in a fashion much like HTML. JSX combines React components and curly brackets to add JS functionality. You’ll notice above, we use {title} to output the JS variable title into the JSX, which will be translated to text in HTML.

Component Props

You may wonder where the Post’s title is coming from, the example below shows you:

import React from 'react';

const App = () => {
  const content = 'This is the post content!'
  return (
    <div>
      <Post title="Title" content={content} />  
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('app'));

We have an App component that uses the Post component and passes to it what are called props. These are very similar to html attributes. The name of the prop is to the left of the equals sign and the value is to the right. For strings, you can use quotes, and for variables you use curly brackets.

Above, the App’s Post component will have two props. A title prop that is “Title” and a content prop that is “This is the post content!”

Encapsulating Logic with Components

Using ReactJS lets us build lots of little pieces of functionality that we can reuse throughout our apps without having to duplicate code. We can even do different things in our components based on the props we pass them. If we needed a list of Blog Posts, instead of recreating the code, we simply use multiple Post components.

ReactDOM Rendering

In the above example, we are using ReactDOM to render our App inside the HTML tag that has an id of app. Without this, our React code would not work because it wasn’t told to render anywhere. For single page applications (SPAs), React manages almost all of the functionality. The HTML could be very simple, and only have one div with the corresponding id. However, you can piece React into a portion of your app using an id and it will start to manage the HTML there through JS!

React Hierarchy

React components are pretty cool and let us organize our JS into hierarchies of components with functionality. We could have an App component that renders a Posts component that renders individual PostPreview components, very similar to a WordPress blog listing page. Maybe we want to include a component that renders social media buttons for the post author inside of those PostPreviews:

const twitterIcon from 'assets/twitter.png';

const SocialButtons = (props) => {
  return (
    <span>
      <a href={props.twitterURL}>
        <img src={twitterIcon} />
      </a>
      // ... more buttons here
    </span>
  );
}

This works great, we have our PostPreview component rendering our SocialButtons component and our PostPreviews now have little social media buttons that users can click and view our social profiles.

The Case for Redux with ReactJS

For our example above, all of our data comes in through the App component and is passed down using props. What if we had multiple requests that the social buttons were distracting and users wanted to ability to toggle them on or off? In our current setup, we’d need to pass a “showSocialButtons” prop all the way down from App to Posts to PostPreview. Imagine if we had SocialButtons in even more nested components. That is a lot of passing props and starts to get confusing because almost none of our components besides SocialButtons need to know this information.

Redux to the Rescue

This is where Redux with React can really shine. It allows us to connect any component to a global state where we can look up information. In our case, we want to look up the value of showSocialButtons, or optionally let the user toggle this on and off. But in reality we can use this for lots of purposes, such as user theme choice, modal states, loading states, timezone preferences, etc.

Introducing Redux to ReactJS

We already learned about the Redux store and how to create and manipulate it in Part 1. After we have finished with all that setup, there are only a couple of things we need to do to use Redux with React.

Create a Redux Store with ReactJS

We first need to create a Redux store using one of our reducers and then allow nested components to access it by using the react-redux Provider:

import React from 'react';
import { Provider } from 'react-redux'
import { createStore } from 'redux'

const initialState = {
  showSocialButtons: true,
}

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'SET_SHOW_SOCIAL_BUTTONS':
      return { ...state, showSocialButtons: action.showSocialButtons };
    default:
      return state;
  }
};

const store = createStore(reducer);

const App = (props) => {
  return (
    <Provider store={store}>
      <Posts posts={props.posts} />  
    </Provider>
  )
}

const posts = [/* posts here */];
ReactDOM.render(<App posts={posts}/>, document.getElementById('app'));

I’ve included all of the necessary pieces in this example so it’s easier to follow. First, our reducer lets us set whether we want to show the social buttons or not. Then, we create a store with the reduce. Finally, inside the App component’s render function wrap the rest of our app’s JSX inside react-redux’s Provider component that allows our components to access the store we created.

Connecting ReactJS components to Redux

Now, we can connect lower nested components directly to our Redux store we have created using the connect function:

const twitterIcon from 'assets/twitter.png';
const { connect } from 'react-redux';

const SocialButtons = (props) => {
  if (!props.showButtons) {
    // display nothing if users don't want to see
    return null;
  }
  
  return (
    <span>
      <a href={props.twitterURL}>
        <img src={twitterIcon} />
      </a>
      {/* More buttons */}
    </span>
  );
};

const mapStateToProps = (state) => ({
  showButtons: state.showSocialButtons,
});

export default connect(mapStateToProps)(SocialButtons);

The key here is the connect function all the way at the bottom. It takes a function, mapStateToProps, that is passed one argument, the Redux state. Any of the returned object’s keys from mapStateToProps will be passed into the SocialButtons component as props. So we’ll have a shiny new prop, “showButtons” that we can use inside of our component.

Arrow Function Helper

You may be a little confused about the parenthesis around the object inside the mapStateToProps function, this is just an ES5 arrow function helper to return a JS object immediately, instead of needing to wrap it in curly brackets and then write a return for the object.

Encapsulating Connected Components

Huzzah! Now, we won’t display the social buttons if the showSocialButtons value in the Redux store is false. And we can reuse this component anywhere in the entire React app and it will dutifully make sure to only display if showSocialButtons is true.

Notice how we connected the SocialButtons component instead of the PostPreview component. We could have done the same check inside of PostPreview, but it is less reusable. In that case, anywhere we need to use a SocialButtons component, we would need to connect the parent component and perform the check. However, if we include this check inside the SocialButtons component, it is neatly packaged and reusable! We can simply import it into any file and it will show or hide correctly.

Interacting with the Redux Store

Now that we’ve seen how powerful reading from the store can be, let’s look at the last example and something we’ll need to do often in our apps, updating the redux store. It’s not enough to just not show our SocialButtons, we need to allow users to disable them. Let’s create a button that will allow our users to turn off SocialButtons:

const { connect } from 'react-redux';

const ToggleSocialButtons = (props) => {
  const buttonText = props.showButtons ? 'Disable' : 'Enable';
  return (
    <button onClick={props.toggleSocialButtons}>{buttonText} Social Buttons</button>
  );
};

const mapStateToProps = (state) => ({
  showButtons: state.showSocialButtons,
});


const mapDispatchToProps = (dispatch, ownProps) => ({
  toggleSocialButtons: () => dispatch({
    type: 'SET_SHOW_SOCIAL_BUTTONS',
    showSocialButtons: !ownProps.showButtons,
  }),
});

export default connect(mapStateToProps, mapDispatchToProps)(ToggleSocialButtons);

In Part 1, we’ve already learned that we change the Redux store by dispatching actions. In order to dispatch actions inside our React components, we need to pass the react-redux connect function an additional function, mapDispatchToProps. This function gives us access to the Redux store’s dispatch function and optionally the component’s props. We return an object where the keys are props that will be added to our components and the values are functions that we can use to dispatch actions.

For our example, we need one prop function, toggleSocialButtons. It dispatches one action that uses the current state of the showSocialButtons value inside the Redux store to toggle it on or off.

Note: In Part 1, we talked about Action Creators, this is a way to structure our actions so that we can reuse them in more than one component. In this case, we would have a file and import a function that looks like:

export const toggleSocialButtons = (showSocialButtons) => ({
    type: 'SET_SHOW_SOCIAL_BUTTONS',
    showSocialButtons: showSocialButtons,
})

Then use it inside the mapDispatchToProps like:

// Old
const mapDispatchToProps = (dispatch, ownProps) => ({
  toggleSocialButtons: () => dispatch({
    type: 'SET_SHOW_SOCIAL_BUTTONS',
    showSocialButtons: !ownProps.showButtons,
  }),
});

// New
const mapDispatchToProps = (dispatch, ownProps) => ({
  toggleSocialButtons: () => dispatch(toggleSocialButtons(!ownProps.showButtons)),
});

Conclusion

Now, we can simply include our ToggleSocialButtons component anywhere in the App, perhaps inside of a UserSettings component, and when clicked it will dispatch a Redux action that our provided store will listen for and update accordingly. When the Redux store values change, the react-redux connect function passes the new props to the ReactJS component. This will automatically re-render our components with the new values from inside the Redux store and the SocialButton Icons will immediate show or hide themselves.

Redux ReactJS Codepen

Finally, I’ve created a codepen that has this example working so that you can check out the code and play around with it. Notice how the dispatch is handled a bit differently in the ConnectedToggleButton.

Part 1 Challenge Solution

At the end of Part 1, we had a challenge to solve. How would we handle adding a post to the Redux store. Let’s jump in!

// This is an action creator that returns the action object
// redux needs to manipulate the store. You pass it a post object
// and it will return a object like:
// const obj = {
//   type: "ADD_POST",
//   post: {
//     id: 1,
//     title: "Title",
//     content: "Content!",
//   }
// }
const addPostAction = (post) => ({
  type: "ADD_POST",
  post,
});

// New reducer with the ADD_POST action handled
function reducer(state = {...initialState}, action) {
  switch (action.type) {
    case 'TOGGLE_MENU':
      return {
        ...state,
        showMenu: !state.showMenu
      };
    case 'REMOVE_POST':
      return {
        ...state,
        posts: state.posts.filter(post => post.id !== action.id)
      };
    // Addition to reducer here!
    case "ADD_POST":
      return {
        ...state,
        // Array.concat returns a new array merging the existing two
        posts: state.posts.concat([action.post]),
      };
    default:
      return state
  }
}

// You would add a post by making one, then calling our addPostAction
// function inside of a store dispatch
const post = {
  id: 5,
  title: "A good post",
  content: "Not a lot of content here"
};
store.dispatch(addPostAction(post));
Share this article:

You might also like: