Module 4: Getting Started with Workouts

In this module, we're going to set up the other side of the application. So far we've been working on login, signup, and logout. Now, we're going to work with what happens once the user is already logged in.

Step 1. Splash Page

Let's start by creating some files and folders. Inside of your home folder, go ahead and create a new file called Splash.js. Inside of src, make a new folder called workouts. Inside of workouts, create a new file called WorkoutIndex.js. The two files we've just created are the parent components for the workout side of the applications.

Once you have done this, navigate inside of Splash.js, and type the following code if you save your code there will be an error:

import React, { Component } from 'react';
import WorkoutIndex from '../workouts/WorkoutIndex';

const Splash = (props) => {
  return (
      <div>
          <WorkoutIndex/>
      </div>
  ) 
}

export default Splash;

The error is because we haven't added any code for the WorkoutIndex.js file, let's resolve that now:

import React, { Component } from 'react';

class WorkoutIndex extends Component{

    render(){
        return (
            <div>
                Workout Index
            </div>
        )
    }
}

export default WorkoutIndex;

So, now we have stubbed out our two components that set up our workout side of the application.

Back to App.js

So, the structure of our application basically has two different sides: 1) if they are not yet logged in/signed up 2) if they are.

Then 2 more sides to those:

1) is the <Auth /> component and children 2) is the <Splash /> component and children.

We need to set up our App.js to handle routing and everything for these two different sides of our application. Let's first make sure we have everything we need imported, your total imports in this file should look like this:

import React, { Component } from 'react';
import Auth from './auth/Auth';
import NavBar from './home/NavBar';
import Splash from './home/Splash';
import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom'

We've got our Auth, Navbar, and Splash components, which is what we need to handle routing and rendering in our App.js.

Next, we need to take care of rendering the appropriate components based on whether or not the user is logged in (and remember to determine this we are checking for a sessionToken). Let's create a function that we can call in our render that takes care of this logic for us, so that we don't have that cluttering up our render(). Create the following function underneath your other functions already in App.js and above your render:

  protectedViews = () => {
    if (this.state.sessionToken === localStorage.getItem('token')) {
      return (
        <Switch>
          <Route path='/' exact>
            <Splash sessionToken={this.state.sessionToken} />
          </Route>
        </Switch>
      )
    } else {
      return (
        <Route path="/auth" >
          <Auth setToken={this.setSessionState}/>
        </Route>
      )
    }
  }

Let's talk through the above function. Essentially all we are doing here is checking to see if there is a sessionToken in the state, and if it matches the token stored in localStorage. If it does, i.e. if the user is logged in, we're going to display the <Splash /> component, we're also passing along our sessionToken as a prop to Splash so that side of the application can use it to make requests to the server. If the user is not logged in, we're going to direct them to the Auth side of the application so that they can get logged in, we can grab the token, and then they can use the workout side of the application.

Lastly, we need to call this function in our render. Change your App.js render to look like the following:

render() {
    return (
      <Router>
        <div>
          <NavBar clickLogout={this.logout} />
          {this.protectedViews()}
        </div>
      </Router>

    );
  }

Notice how we are displaying our <NavBar /> all the time regardless of whether or not the user is logged in. Everything else depends on whether or not they are logged in. It's nice to have this all in a function, so that when we add more complexity to our application our render can stay the same, and the new logic can be in the protectedViews function.

At this point, this is the code for the completed App.js.

import React, { Component } from 'react';
import Auth from './auth/Auth';
import NavBar from './home/NavBar';
import Splash from './home/Splash';
import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom';

class App extends Component {
  constructor() {
    super();
    this.state = {
      sessionToken: '',
    }
  }

  componentWillMount() {
    const token = localStorage.getItem('token')
    if (token && !this.state.sessionToken) {
      this.setState({ sessionToken: token });
    }
  }

  setSessionState = (token) => {
    localStorage.setItem('token', token);
    this.setState({ sessionToken: token });
  }

  logout = () => {
    this.setState({ sessionToken: ''});
    localStorage.clear();
  }


  protectedViews = () => {
    if (this.state.sessionToken === localStorage.getItem('token')) {
      return (
        <Switch>
          <Route path='/' exact>
            <Splash sessionToken={this.state.sessionToken} />
          </Route>
        </Switch>
      )
    } else {
      return (
        <Route path="/auth" >
          <Auth setToken={this.setSessionState}/>
        </Route>
      )
    }

  }

  render() {
    return (
      <Router>
        <div>
          <NavBar clickLogout={this.logout} />
          {this.protectedViews()}
        </div>
      </Router>

    );
  }
}

export default App;

Now, that in our App.js we passed our sessionToken to the <Splash /> component we can pass that along to our WorkoutIndex inside of Splash.js. Change Splash.js render to look like this:

import React from 'react';
import WorkoutIndex from '../workouts/WorkoutIndex';

const Splash = (props) => {
    return (
        <div>
            <WorkoutIndex token={props.sessionToken} />
        </div>
    )
}

export default Splash;

Step 2. Workout Start

Next, let's start working on our main parent for the workout side of the application, WorkoutIndex.js.

First, import all of our components we just created, and react and reactstrap:

import React from 'react';
import { Container, Row, Col } from 'reactstrap';

Next, we are going to do is build out our whole constructor. Type out the following code:

constructor(props) {
    super(props)
    this.state = {
      workouts: []
    }
  }

Things to note about the constructor:

  • we set up our state for the component, we have an array of workouts

Awesome. Now we are going to go ahead and build out a simple fetch workouts function:

fetchWorkouts = () => {
    fetch("http://localhost:3000/api/log", {
        method: 'GET',
        headers: new Headers({
            'Content-Type': 'application/json',
            'Authorization': this.props.token
        })
    })
        .then((res) => res.json())
        .then((logData) => {
            return this.setState({ workouts: logData })
        })
}

What we are doing in fetchWorkouts is getting our workouts from our server. Note that we are including the Authorization header, and our token that was passed as a prop from above. Remember that we need our Authorization header so that the server can know who is making the requests, and understand them. Also notice that after we get back information from the server, we are saving the workout information to our state. Next, we need to actually call this function to run when our component mounts. Remember that componentDidMount is the best place for API calls because it is only called once.

componentDidMount() {
    this.fetchWorkouts()
}

Then we update our render with things that are not built out yet, but set up so that we can use them:

render() {
    return (
      <Container>
        <Row>
          <Col md="3">
            {/* the create component will go here*/}
          </Col>
          <Col md="9">
            <h2>Log a workout to see table this will be added in the upcoming modules</h2>
          </Col>
        </Row>
      </Container>
    )
  }

Finished Code:

import React from 'react';
import WorkoutCreate from './WorkoutCreate';
import { Container, Row, Col } from 'reactstrap';

class WorkoutIndex extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      workouts: []
    }
  }

  componentWillMount() {
    this.fetchWorkouts()
  }

  fetchWorkouts = () => {
    fetch("http://localhost:3000/api/log", {
      method: 'GET',
      headers: new Headers({
        'Content-Type': 'application/json',
        'Authorization': this.props.token
      })
    })
      .then((res) => res.json())
      .then((logData) => {
        return this.setState({ workouts: logData })
      })
  }

  render() {
    return (
      <Container>
        <Row>
          <Col md="3">
            {/* the create component will go here*/}
          </Col>
          <Col md="9">
            <h2>Log a workout to see table this will be added in the upcoming modules</h2>
          </Col>
        </Row>
      </Container>
    )
  }
}

export default WorkoutIndex

Last updated