Module 6: Workout Table

In this module, we'll create a component that displays our workout and has buttons to edit and delete workouts.

Step 1. Setting Up Our Component

Since we defined our functions that make API calls in our WorkoutIndex we can make this component a functional component, since we don't need to deal with the state at all. Let's add some code to WorkoutTable.js, start it off with the following:

import React from 'react';
import { Table, Button } from 'reactstrap';

const WorkoutTable = (props) => { //1
    return (
        <div>
        </div>
    )
}

export default WorkoutTable;
  1. Notice that we're including props so that we can use them here.

Step 2. Return

Since we're dealing with a functional component, we really just need to work on our return, to correctly display the workout information in the format that we want it in.

Change your return to look like this:

    return (
        <div>
            <h3>Workout History</h3>
            <hr />
            <Table striped>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>Result</th>
                        <th>Definition</th>
                        <th>Description</th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>
                    {/* 1 */}
                </tbody>
            </Table>
        </div>
    );

Analysis

So, here we're setting up our table to hold our information, with our header, and our table started. 1. Right now our body is empty though, so we need to figure out a way to take our workout information that we're getting from above as a prop, and map it out into the correct format. Put the following code in between the <tbody> and </tbody> tags.

{
    props.workouts.map((workout, id) => { //1
        return ( //2
            <tr key={id}> //3
                <th scope="row">{workout.id}</th>
                <td>{workout.result}</td>
                <td>{workout.definition}</td>
                <td>{workout.description}</td>
                <td>
                    //4
                    <Button id={workout.id} onClick={props.delete} color="danger">Delete</Button>| //4
                    <Button id={workout.id} onClick={e => props.update(e, workout)} color="warning">Update</Button>
                </td>
            </tr>
        )
    })
}

Analysis

  1. We're using map, which iterates through an array and performs the same operation on each item in the array, in this case, we're formatting each item of the array as a table row.

  2. See how we used to return, this is important when doing a multi-line map, or it won't know what to return.

  3. Notice how we need a key because react requires unique keys for elements created that are otherwise identical.

  4. Check out the two buttons, one is for deleting, and one is for updating. Notice how onClick we're calling functions that come from the props, that we defined in our WorkoutIndex

Back to WorkoutIndex

In WorkoutIndex.js we need to actually render this component.

Let's first import our component:

import WorkoutsTable from './WorkoutsTable';

Next, we need to create a function that'll allow us to delete workouts. In the workout table we see that we have something called props.delete but we haven't yet created a function that'll allow us to delete. Let's do that now. Create the following function beneath fetchWorkouts, call it workoutDelete.

 workoutDelete = (event) => {
    fetch(`http://localhost:3000/api/log/${event.target.id}`, {
      method: 'DELETE',
      body: JSON.stringify({ log: { id: event.target.id } }),
      headers: new Headers({
        'Content-Type': 'application/json',
        'Authorization': this.props.token
      })
    })
    .then((res) => this.fetchWorkouts())
  }

Analysis

  1. Notice that we're passing the event to this function, so that we can use it in #3, to grab the information from the event.

  2. The big difference between this method and things like the create method, is our method is now DELETE.

  3. Notice that in the body, we're specifically passing the id, which is all we need on the backend to delete a workout. We need to be able to grab the id of the workout and delete it.

  4. Again, we're passing our token to our Authorization header, so that we can make API calls.

  5. After the delete is done, we're fetching workouts again, so that we can update our display.

Next, we'll need to work on our return to actually get our Workouts Table to display!

Inside of the render() but before the return(), we need to add some code to format our workouts for us.

    const workouts = this.state.workouts.length >= 1 ?    //1
      //2 
      <WorkoutsTable workouts={this.state.workouts} //3
       delete={this.workoutDelete} update={this.setUpdatedWorkout} /> : <h2>Log a workout to see table</h2>

Analysis:

  1. first thing we're doing is checking to see if the workouts array in the state of our WorkoutIndex.js is greater than or equal or 1, i.e are there are any workouts. Remember when the component mounts, we're grabbing all the workouts in a get, so they should be there if there are any in the DB.

  2. If there are workouts, we display <WorkoutsTable>, if not we say "log a workout to see table"

  3. Notice all of the props that we are sending to WorkoutsTable, the workouts (which we need in WorkoutsTable to render them), the update function (so that we can call it), and the delete function (so that we can call it in the table).

Then we need to actually put this constant we just created into the render in WorkoutIndex. See the adding workouts comment below. This is the render code of WorkoutIndex at this point.

render() {
    const workouts = this.state.workouts.length >= 1 ?
      <WorkoutsTable workouts={this.state.workouts} delete={this.workoutDelete} update={this.setUpdatedWorkout} /> :
      <h2>Log a workout to see table</h2>
    return (
      <Container>
        <Row>
          <Col md="3">
            <WorkoutCreate token={this.props.token} updateWorkoutsArray={this.fetchWorkouts} />
          </Col>
            {/* adding workouts */}
          <Col md="9">
            {workouts}
          </Col>
        </Row>
      </Container>
    )
  }

This is What it Should Look Like:

Now, after we've linked everything up in WorkoutIndex.js, we should see this:

Finished Code

import React from 'react';
import { Table, Button } from 'reactstrap';


const WorkoutTable = (props) => {

    return (
        <div>
            <h3>Workout History</h3>
            <hr />
            <Table striped>
                <thead>
                    <tr>
                        <th>#</th>
                        <th>Result</th>
                        <th>Definition</th>
                        <th>Description</th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>
                    {
                        props.workouts.map((workout, id) => {
                            return (
                                <tr key={id}>
                                    <th scope="row">{workout.id}</th>
                                    <td>{workout.result}</td>
                                    <td>{workout.definition}</td>
                                    <td>{workout.description}</td>
                                    <td>
                                        <Button id={workout.id} onClick={props.delete} color="danger">Delete</Button>|
                                        <Button id={workout.id} onClick={e => props.update(e, workout)} color="warning">Update</Button>
                                    </td>
                                </tr>
                            )
                        })
                    }
                </tbody>
            </Table>
        </div>
    );
}

export default WorkoutTable;

Last, we're going to work on our Workout Edit!

Last updated