Module 2: Token

In this module, we will focus on adding authentication to our app by saving our token that we get from the server upon sign up or login.

Step 1. Signup

constructor additions

Let's go into the Signup.js file and add some things starting with the constructor:

class Signup extends Component {
    constructor(props) { //2
        super(props)
        this.state = {
            username: '',  //1
            password: ''
        };
    }

Analysis

  1. Note that we've added username and password to our state. We want to set these items to empty strings to set the initial value for the state of those properties. Simply put, when a user starts the application those properties should not have values.

handleChange method

Under the constructor(note that ours is collapsed for brevity) create a handleChange function:

class Signup extends Component {
    constructor(props) {
        super(props)
        this.state = {
            username: '',
            password: ''
        };
    }

    // Add the function here:
                    //1
    handleChange = (event) => {
                //2
        this.setState({
                //3                     //4
            [event.target.name]: event.target.value,
        });
    }

Analysis

Each time the user types inside of the input, we want the state to change: 1. This function takes in the event (which is captured when the user types something into the input). 2. We call the setState method that will allow us to change the state of the application. 3. It takes the name field of the target, which in our case is username and password. Remember how we told you to note the name attribute/property in our last module? Here's a screenshot to help you visualize this:

  1. We grab the value, which is what the user typed into the input fields for username and password.

handleChange method

Now let's add this handleChange function to our username and password inputs in the jsx. NOTE: SOME CODE HAS BEEN OMITTED FOR BREVITY IN THE CODE BELOW. You'll need to add onChange={this.handleChange} as in the following code:

<FormGroup>
    <Input id="username" type="text" name="username" placeholder="enter username" onChange={this.handleChange} />
</FormGroup>
<FormGroup>
    <Input id="su_password" type="password" name="password" placeholder="enter password" onChange={this.handleChange} />
</FormGroup>

Testing

  1. Open up your console in the web browser and you will see the changes as you type in your React Dev Tools.

  2. Right click inspect on whatever element you want to see the state of twice. In this case you will right click on the Sign Up element.

  3. React devtools should go to it automatically. Make sure you're on <Signup> so that you can see the right state. This is a good step to take every time when changing the state, to make sure things are functioning like you want them to.

  4. You should see the following:

handleSubmit

Now we want to create a handleSubmit function. This function will eventually serve to do a POST for our user information in a request and then get the sessionToken back in a response. For now, let's just go ahead and build a really basic handleSubmit function directly under handleChange:

handleSubmit = (event) => {
    console.log(this.state)
    event.preventDefault()
}

Analysis

Notice above that we're taking in an event, and we are preventing default, which in this instance will prevent our page from refreshing when we submit the form. Also, note that handleSubmit and handleChange are both arrow functions here so that we don't have to worry about binding them in the constructor. If you DO NOT make them as arrow functions you must bind them in the constructor, so that you can access things like this.setState.

Now that we have a basic function, let's plug this into where we will be using it, so that we can console log things out and make sure that they are working correctly.

Form Submission

  1. You are still inside of signup.js.

  2. Go to the form in the render function.

  3. Add the onSubmit field to the top of your form and make add the call for handleSubmit:

<Form onSubmit={this.handleSubmit} > 
    <FormGroup>
  1. Still inside the render function, in the Form, add in a standard button after the FormGroup closing tag and just before the Form closes:

         </FormGroup>
         <Button type="submit"> Submit </Button>                    
     </Form>
  2. Now we have a basic form that logs our message to the console upon submitting.

Step 2. sessionToken

Let's review what we need to do: 1. In our Signup and Login components, once the user submits their information we want to log them in and store the session token. 2. Remember that we set up the capability to store and set the token in the localStorage in our App.js, particularly by writing the setSessionState method. Be sure to take a minute to review that file/code a little. 3. Now, we just need to pass the ability to set the sessionToken down to our Login and Signup components. We don't need to create another function to do this. We simply pass the token down as a prop. 4. The reasons for making the function in App.js and not in both Login and Signup is that we want to store the sessionToken in the parent and not all over the application. We also don't want duplicate code. Let's go ahead and see how we can pass our functions as a prop throughout our application.

Passing Down Props

  1. Go to App.js and pass in our setSessionState as a prop to our Auth component. so that we can then pass this function to the children.

    Login and Signup:

    render() {
     return (
       <Router>
         <div>
           <SiteBar />
           <Auth setToken={this.setSessionState} />
         </div>
       </Router>
     );
    }
  2. Now head into your Auth.js. Make the following changes to the Auth component:

               //1
const Auth = (props) => {
    return (
        <Container className="auth-container">
            <Row>
                <Col md="6">
                                {2}   {3}    {4}
                   <Signup setToken={props.setToken}/>
                </Col>
                <Col md="6" className="login-col">
                                    {5}
                    <Login setToken={props.setToken}/>
                </Col>
            </Row>
        </Container>
    )
}
  1. We are adding props to our parameter. Remember that this is a functional component, like a function it can take in arguments to be used throughout the function.

  2. We create a property(prop) called setToken. This will allow us to pass the token down to our Signup function. This is not the same as the setToken in the App.js component. It's a property that will be associated with a lower component, and it is named the same for clearly bridging the props through the unidirectional flow.

  3. These props are tethered to the props parameter in the parens above.

  4. When we use the dot accessor on that props variable, we can use access the properties from App.js. Look where we call the Auth component in App.js: <Auth setToken={this.setSessionState}/>

  5. We do the same process of on our Login Call.

handleSubmit additions

Now we have access to the function that we wrote in App.js in our Signup component. Let's open our Signup.js and update our handleSubmit function so that we can properly create a token:

handleSubmit = (event) => {
    //1
    fetch("http://localhost:3000/api/user", {
        method: 'POST', //2
        body: JSON.stringify({user:this.state}), //3
        headers: new Headers({
            'Content-Type': 'application/json' //4
        })
    }).then(
        (response) => response.json() //5
    ).then((data) => {
        this.props.setToken(data.sessionToken) //6
    }) 
    event.preventDefault()
}

Analysis

Let's look at what the above function is doing: 1. We're sending a fetch request to the endpoint determined in our server, that is where we go to signup. Note that this endpoint is determined by whatever backend you're using. In your own server backend, it's declared in app.js if you forget what you named your routes. Take some time to review your server now. A good practice is to have endpoints available in notes. 2. The method of the fetch is a POST. 3. We're including a body with our state information set as user. This again correlates to the backend. If your server is expecting information in this format: req.body.user.username and req.body.user.password, then the above will work. When making future applications this is what has to match what the backend is expecting. If the backend is expecting req.body.user.username and req.body.user.password and I send it req.body.spongebob.pineapple, it doesn't know how to handle that information. 4. We're including the header Content-Type set to application/json. This let's our server know what type of information we are sending to it, so it can decide if it can handle it and what to do with it. 5. We're resolving the promise from fetch and calling .json(), allowing us to turn the response into JSON when it resolves. 6. We're resolving the .json() promise, and taking the data we get back from the server and then calling our setToken function with the sessionToken we get back in the data object.

Review

Let's look at another breakdown of the props data getting passed down through the application: 1. In App.js we set the state of our token as an empty string. 2. In App.js we pass our setSessionState function as a prop from our App.js to our Auth.js in a property called setToken. 3. In Auth.js we then pass our token as a prop to Signup.js.

Challenge: No username? Return a message:

For this challenge, we want to display a message to the user if the user hasn't added a username, this is a basic version of form validation. You don't have to have completed this before moving on. It can be done at any time.

You should see something like this:

Last updated