Journey into React Part 6: Managing state and connecting to an Api with Redux and Axios

Our React application is coming along nicely. We've got a basic front end all set up and we're ready to make a call to the test api endpoint that we created in the last article. This tutorial is going to focus on setting up and using Redux as well as Axios so that you can make a call to that server. We'll walk through a very simple reducer, touch on actions and action types, and finally get some information from our server and display it to our clients.

When you're finished today your application will: have a link that triggers an action, which asks our server for a string, then uses the updated application state to display the message to the client. A very similar process to this one will be repeated numerous times going forward as we build features into our project. This procedure will often vary in its complexity so getting a good understanding of the basics is very important.

You can follow and view the complete source code for this tutorial here!

Installing new dependencies and creating directories

We're going to be using quite a few new libraries today so first things first let's get them installed and talk a bit about what they do.

npm install --save axios react-redux redux redux-thunk  

axios axios allows us to make XMLHttp requests to our server. It also supports promises which are crucial to making the powerful and dynamic applications that we have in mind.

redux Redux is a predictable and simple way to manage your application state. It has a small API and allows for some incredible and powerful features like time-travel, hot reloading, and more.

react-redux Brings in the react bindings that we need to use Redux in our application. By default, Redux is not built for React, or any framework really.

redux-thunk Thunk is middleware that allows us to use actions to return a function, delaying the dispatch of those actions, or only dispatching them if certain conditions are met.

Next, before we can get started let's lay the groundwork for some new files we'll be creating. In your src directory create both an actions and a reducers folder. We'll leave them empty for now.

Pit stop: how Redux and React work together

I found this great diagram below that visualizes the way that Redux flows and interacts with our application. Wrapping your head around what we'll be creating next can be tricky, so let's start with understanding what it is we're making.

As you can see the application's front-end consists of 5 parts:

The UI This is what the client sees and interacts with. When they do something in the application (like update their username) it triggers an action.

Actions The action then does whatever it's designed to do. Afterward, it sends the results to the reducers. In this example, it'd make an API request to update the username and then it would receive a confirmation message.

Reducers Once the action has completed, it dispatches that information to the reducer by declaring an "action type" and attaching a payload. The action type tells the reducers what has happened, for example UPDATE_USERNAME, and the payload would be the confirmation message.

Store and State The store contains the application state, which now consists of the confirmation message. Since the state has changed, it then renders the message back to the UI letting the user know it was successful.

Now that we've got this out of the way let's go ahead and start wiring up our actions and reducers.

Putting together the reducer

Let's start by creating the reducer we're going to use for this example. Using a bit of forward thinking, we're going to call this reducer our auth_reducer and in the next tutorial, we'll use it to handle our login and authorization actions. For today it's just going to work with a test action.

Create your first action-type by creating the file types.js in your actions directory. In this file we are going to export simply a constant that describes the action we'll be performing:

export const TEST_ACTION = 'test_action';  

After you've saved your types.js file, create another one titled auth_reducer.js in your reducers directory and open it up. Now we need to import our action-type that we just made:

import { TEST_ACTION } from '../actions/types';  

Next, define the initial state that will be stored. In this case, the message will be empty:

const INTIAL_STATE = { message: ''};  

We're only going to be receiving the "hello world" message from our API today, so our initial state object will only consist of a single item. Lastly, we export a function that contains a switch statement and returns the state.

export default function (state = INTIAL_STATE, action) {  
  switch(action.type) {
    case TEST_ACTION:
      return { ...state, message: action.payload.message };
  }
  return state;
}

This function takes our initial state and the action and tests to see what action was performed. If it's our currently uncreated TEST_ACTION it updates our state with the payload from our API.

Combining reducers and exporting them

To finalize setting up our reducer we need to import it into a new file titled index.js inside the reducers directory. This file collects all of our reducers into a singular location, combines them with the combineReducers function and exports them as the 'root reducer'.

import { combineReducers } from 'redux';  
import AuthReducer from './auth_reducer';

 const rootReducer = combineReducers({
   auth: AuthReducer,
 });

 export default rootReducer;

Create the action that will send information to the reducer

Now we need to make the action itself and tell it to dispatch to the reducer. Create a new file titled index.js in the actions directory and import axios (to make our HTTP requests) and the action-type we made. Also, define a constant that will contain the root API URL we set in our last tutorial. This will make our axios calls smaller and easier as our API routes get more complex.

import axios from 'axios';  
import { TEST_ACTION } from './types';

const API_URL = 'http://localhost:3000/api';  

Axios is simple enough to set up, and we'll be using promises to dispatch the action to the reducers after the get request has been completed. We'll also include a quick catch to handle any errors we might get.

export function testAction() {  
  return function(dispatch) {
    axios.get(`${API_URL}/helloworld`)
    .then(response => {
      dispatch({
        type: TEST_ACTION,
        payload: response.data
      });
    })
    .catch((error) => {
      console.log(error);
    })
  }
}

As you can see, we use axios.get to specify the request. This function takes, at the minimum, the URL parameter which should point to the route we want to call on our server. .then takes the response and uses an arrow function to dispatch the type and payload to our reducers. .catch handles any errors we may run into.

Creating a link to trigger the action

Open up the Dashboard file you created previously so that you can call the action we just made. Make sure you import your testAction at the top.

import { testAction } from '../actions/index.js';  

Create a new method that will call the test action you just imported:

  handleClickHello() {
    this.props.testAction();
  }

Now add a link to the render method that will call handleClickHello on click.

  render() {
    return (
      <div>
        <h4>This is the dashboard</h4>
        <a onClick={this.handleClickHello.bind(this)}>Knock Knock</a>
      </div>
    );
  }

Connect your component to the application state

It's important to give our Dashboard access to our store and make calling data from it easy. So let's go ahead and do that now. Import the connect function from react-redux into your dashboard file.

import { connect } from 'react-redux';  

Now at the bottom, outside the scope our component, we're going to create a function that will map our application state to the components props.

function mapStateToProps(state) {  
  return {
    auth: state.auth
  };
}

We only need to map out the auth state (from our src/reducers/index.js file) because, well, that's all we have. Lastly, we need to connect our component to our store:

export default connect(mapStateToProps, { testAction })(Dashboard);  

Be sure to remove the export default from our component so that it reads:

class Dashboard extends Component {  
  //component
}

Lastly, let's throw in a reference to the message itself inside the component so that we can see whatever message is returned to us from the server. You can now manipulate and call the message like any other variable in your component, referencing it through this.props.auth.

  render() {
    return (
      <div>
        <h4>This is the dashboard</h4>
        <a onClick={this.handleClickHello.bind(this)}>Knock Knock</a>
        <h3>{this.props.auth.message}</h3>
      </div>
    );
  }

Supplying the provider and store to our application

The final thing we need to wire up now is our application itself. We need to create the store which will hold our reducers and we also need to wrap our app in the Provider function from react-redux. We need to import all these new libraries, so let's start there. In your src/index.js file update your dependencies to reflect this:

import { createStore, applyMiddleware } from 'redux';  
import { Provider } from 'react-redux';  
import React from 'react';  
import ReactDOM from 'react-dom';  
import reduxThunk from 'redux-thunk';  
import { Router, browserHistory } from 'react-router';

import reducers from './reducers/index';  

Next we need to create the store using functions from redux:

 const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
 const store = createStoreWithMiddleware(reducers);

And lastly, wrap the application in the Provider and set the store:

ReactDOM.render(  
  <Provider store={store}>
    <Router history={browserHistory} routes={routes} />
  </Provider>,
  document.querySelector('#app'));

Testing your project

We've done a lot of work here today. Let's see if it has all come together. Start both your server and your client application, and navigate to http://localhost:8080/ (the dashboard). Now, as long as everything is set up correctly when you click the "knock knock" link, below it, you should see the "hello world" message that our server returned. Congratulations, your client is talking to your very own API!

State of the tutorial series

This segment is a major turning point in the series. We've covered most of the fundamentals to creating your own project, including the basics of working in React, making your own RESTful API, and managing application state, stores and redux. You could for all intents and purposes take the information you've learned in the last 6 articles and create an entire React based website or app. So, as we move forward, the topics of discussion will be more advanced, covering things such as JWT authentication and mongoDB management. We'll move quickly through creating new components, reducers, routes, and actions. But if you have any questions you can always ask in the comments or refer back to these posts.

I am really excited to see the outcome of our app, and I expect there to be another 4 or 5 parts before we see the final product come together. I hope to see you there, and thanks for reading!

David Meents

React.js developer, web designer, and business owner.

Subscribe to David Meents

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!
comments powered by Disqus