« Previous Tutorial Next tutorial »

Ready to move some more functionality into our authentication action file? It’s a similar process to the previous tutorial, with a couple of twists, which we’ll cover along the way. Let’s do this!

Start by opening /src/actions/authentication.js. At the top, add in our pollyfill which helps make sure fetch() works in more browsers:

import 'whatwg-fetch';

Leave that file for a bit and switch over to /src/components/account/LoginPageContainer.jsx. Go ahead and delete the whatwg-fetch import at the top, since we’re not going to be using it here anymore. Then it’s time to cut-n-paste again. Highlight lines 24 to 73—our entire attemptLogin method—and cut ‘em. Then head back to /src/actions/authentications.js, add a padding line below line 11, and type this comment:

// Log User In

Then, below that, past your code. As you probably remember from the previous tutorial, this code needs some adjustment to work in this file. Let’s start by changing line 14 from:

  async attemptLogIn(userData) {

to:

export function logUserIn(userData) {

Note that we’re removing the spacing there, but don’t change the spacing on the rest of it. Instead, below that first line, add the following:

  return async (dispatch) => {

There. Now the rest of our indentation lines up. Glorious. Let’s celebrate by deleting lines 16 to 23, because we don’t need them anymore. All right, we’re getting into new territory for this tutorial. Instead of giving you explicit code examples for this next part, I’m just going to tell you what to do, and then I’ll give you the entire code block as it should look when it’s done. So, here are the steps you need to take:

  1. Add a closing brace below line 56 to properly close our newly-encapsulated function.
  2. Add a semicolon at the end of line 56.
  3. Remove the word “Action” from all of the function calls in this block that have it. So for example, incrementProgressAction(); becomes incrementProgress();. This should affect 6 lines.
  4. Wrap all six of those functions in dispatches. Don’t forget the parentheses! So, for example, loginFailure(new Error(error)); becomes dispatch(loginFailure(new Error(error)));
  5. Remove line 45 (this.setState({ redirect: true });) entirely.
  6. On what is now line 54, add a return before the dispatch.

Here’s what your block should look like when you’re done. Don’t be cheap and just copy-paste this! You’ll learn more by making the changes above yourself!

// Log User In
export function logUserIn(userData) {
  return async (dispatch) => {
    // turn on spinner
    dispatch(incrementProgress());

    // register that a login attempt is being made
    dispatch(loginAttempt());

    // contact login API
    await fetch(
      // where to contact
      '/api/authentication/login',
      // what to send
      {
        method: 'POST',
        body: JSON.stringify(userData),
        headers: {
          'Content-Type': 'application/json',
        },
        credentials: 'same-origin',
      },
    )
    .then((response) => {
      if (response.status === 200) {
        return response.json();
      }
      return null;
    })
    .then((json) => {
      if (json) {
        dispatch(loginSuccess(json));
      } else {
        dispatch(loginFailure(new Error('Authentication Failed')));
      }
    })
    .catch((error) => {
      dispatch(loginFailure(new Error(error)));
    });

    // turn off spinner
    return dispatch(decrementProgress());
  };
}

With that done, save the file, and head back to /src/components/account/LoginPageContainer.jsx. We’ve performed surgery on this file, and as with all surgery, you need to sew up the holes once you’ve cut them. At least, I’m pretty sure that’s how it works. I am not a surgeon, and if you ever see me snapping on gloves in your operating room, you should leap from the table and run screaming down the hall, even if your hospital gown shows off your butt to everyone you pass.

Anyway, let’s repair our file. For starters, delete any excess whitespace your cutting may have caused. After that, clean up your imports so they look like this:

import React from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';

import LoginPage from './LoginPage';

And then, below line three, add the following code:

import { logUserIn } from '../../actions/authentication';

Next, we can delete the component state from our constructor. “But how are we going to know to redirect our user,” you ask? Well, when we built our authentication reducer, we brilliantly added an “isLoggedIn” boolean into our user object. We’re going to map that state to props, and check against that instead, in just a minute. For now, just delete lines 14 to 18.

Much like with logging out, we still need a small method to pass down to LoginPage as a prop, so below the constructor but above the render block, add the following:

  logUserInFunction(userData) {
    const { dispatch } = this.props;
    dispatch(logUserIn(userData));
  }

Then change line 13 to bind to that function instead of our old one, like this:

    this.logUserInFunction = this.logUserInFunction.bind(this);

And change line 32 to reference it, like this:

        <LoginPage loginFunction={this.logUserInFunction} />

Now skip down to the end of the file, and delete the entire mapDispatchToProps block. We’re going to replace it with the following code:

function mapStateToProps(state) {
  return {
    authentication: state.authentication,
  };
}

And then change line 44, the final line in our file to reflect that, like this:

export default connect(mapStateToProps)(LoginPageContainer);

Note: we replaced the null with mapStateToProps and got rid of mapDispatchToProps, which is why connect has gone from taking two arguments to just one. Because connect expects mapStateToProps first, we have to pass null in order for it to move on to mapDispatchToProps. But now we’re passing mapStateToProps , and because of the way function arguments work, we don’t have to bother specifying null for anything that follows, because simply by not defining them, we get the same result.

All right, last step, we need to check this state in our component. So change line 22 to this:

    const { authentication } = this.props;

And then change line 24 to this:

    if (authentication.isLoggedIn) {

That’s it! We’ve patched up this file. Save it, so it can head off to the recovery room and drink milkshakes and watch TV until it’s ready to leave the hospital. H–have I carried this metaphor too far? Let’s move on.

You can test this in a browser, of course. Reload your log in page, then log in with a credentialed user. It should do … exactly the same thing as it did before. That’s what we’re looking for. Such is the joy of code refactoring.

In the next tutorial, we’ll move one final fetch block to our action file, and make a few other tweaks. Until then!

« Previous Tutorial Next tutorial »