Login - Part 2


In this video we are continuing on with our Login journey.

By the end of the previous video we had our Login Form component, which is used by the Login Page container. From the Login Page we had defined a basic function that simply console.log'd the output of the form submission.

Now, we must take this information and create a Flux Standard Action (FSA), which is a fancy name for a plain old JavaScript object that conforms to an expected 'shape'. That shape is:

const fsa = {
  type: 'some-string-here'
  payload: {
    entirely: 'optional'
  }
};

You can read more about the Flux Standard Action here.

To give you a better understanding of how it might look in our circumstances:

const ourFluxStandardAction = {
  type: 'login-requested'
  payload: {
    username: "peter",
    password: "testpass"
  }
};

In our case we won't need to create a variable to hold the FSA, instead we can simply use it inline, as we shall see momentarily.

Having an FSA isn't really very useful - it is, after all, just a plain old object we created - until we call the Redux store's dispatch function, passing the FSA in as the only argument.

From here on in, I will simply refer to a Flux Standard Action as an Action.

In Redux, the application's state is read-only.

The only way to change the application's state is by dispatching one of these Actions.

Simply dispatching an Action isn't enough by itself. You must write the code that when given the current state, and the information from the Action, can return the next state.

This code lives in the Reducer.

Your Reducer may be a single file, or more likely, many small files (functions) than are combined together.

A reducer function must be pure. This means it cannot have side effects.

If you had a simple function - add(x,y) - which simply added two numbers together and returned the result, this would be an example of a pure function. If you pass in 2, and 2, you are always - barring some changes to the laws of mathematics - going to get 4 in return.

However, if your function is called 1ogin(username, password), and this function involves a HTTP request to your back end API, then it is very unlikely you can always guarantee one single outcome. A login attempt may fail due to a bad username, bad password, or the API being down. This really plays havoc with this core principle of Redux. This network call is an example of a side effect.

This whole problem space is why we will be using Redux Saga.

Anyway, we haven't coded our reducer yet, so don't worry about this just now.

Whilst we don't yet have our reducer, we do have Redux in our project. This is important as we must, somehow, gain access to this dispatch function. This is achieved by using connect. In other words, we need to connect our component(s) to the Redux store.

Let's see how this would change our LoginPage:

// /src/containers/LoginPage.js

import React, {Component} from 'react';
import {connect} from 'react-redux';
import LoginForm from '../components/LoginForm';

class LoginPage extends Component {

  doLogin(formData) {
    console.log('this.props', this.props);
  }

  render() {
    return (
      <LoginForm
        onSubmit={this.doLogin.bind(this)}
      />
    );
  }

}

export default connect()(LoginPage);

Once our LoginPage is connected to the Redux store, this component's props will contain a new function - the dispatch function.

Knowing this, we can use this function to dispatch our Action:

// /src/containers/LoginPage.js

import React, {Component} from 'react';
import {connect} from 'react-redux';
import LoginForm from '../components/LoginForm';
import * as types from '../constants/actionTypes';

class LoginPage extends Component {

  doLogin(formData) {
    this.props.dispatch({
      type: types.LOGIN__REQUESTED,
      payload: {
        username: formData.username,
        password: formData.password
      }
    });
  }

  render() {
    return (
      <LoginForm
        onSubmit={this.doLogin.bind(this)}
      />
    );
  }

}

export default connect()(LoginPage);

Note here that now we pass in this Action object directly into the dispatch function. We don't need to declare it as its own variable first as shown earlier, that was simply for demonstrative purposes.

Also interesting to note here is that this Action only describes that we are requesting login. It doesn't describe a successful login, or a failed login. As we shall soon see, these would be additional, distinct Actions in their own right.

The last thing to note here is that a const has been defined and exported from a file called src/constants/actionTypes.js. This file contains many exported values, and so using the import * as ... syntax allows them all to be pulled in at once, rather than individually. It also helps when writing tests, as we will see shortly.

At this point we should be using the dispatch function provided by the Redux store to send an Action containing information telling other interested parties that this is a login request, and that the login request has been made with the username and password of whatever was submitted from the form.

In the next video we will do something interesting with this data, but hopefully at this stage you can see how this crucial piece of your application fits together.

Code For This Course

Get the code for this course.

Code For This Video

Get the code for this video.

Episodes