Module 5: Workout Create

Step 1. Setting Up Our Component

To set up our functionality to create a workout, let's put some code in our component WorkoutCreate. Start it off with the following code:

import React, { Component } from 'react';
import { Button, Form, FormGroup, Label, Input } from 'reactstrap';

class WorkoutCreate extends Component {
    constructor(props) {
        super(props)
        this.state = {
            id:'',
            result: '',
            description: '',
            definition: ''
        };
    }

    render() {
        return (
            <div>
                WorkoutCreate Component
            </div>
        )
    }
}

Let's talk about how we set up our state. Remember that state is like a storage container for information within your component. Here we're setting up our state to be able to store the information we need to create a workout, result, description and def. Next, we need to create a way to change the state .

Step 2. Change State Based on Input

We need to create some functions to change our state based on user input. So when the user types, we want to capture that information and change our state based off of it. So the first thing we're going to do is create a function called handleChange. Check out the code below. Chances are you've seen this exact function before, we're just starting by capturing an event (in this case when the user types), and grabbing the name of the field, and the value they've typed in. Note that the name of our input must match the corresponding name in our state. I.E. in this example, the result needs to match result, etc.

handleChange = (event) => {
    this.setState({
        [event.target.name]: event.target.value
    })
}

Step 3. Posting to the Database

Next, we want to be able to, when they click the button to run a POST request to create our workout. We need to create a function that can do this. Write the following code beneath your handleChange function.

handleSubmit = (event) => {
    event.preventDefault();
    fetch(`http://localhost:3000/api/log/`, {
        method: 'POST',
        body: JSON.stringify({ log: this.state }),
        headers: new Headers({
            'Content-Type': 'application/json',
            'Authorization': this.props.token
        })
    })
    .then((res) => res.json())
    .then((logData) => {
        this.props.updateWorkoutsArray();
        this.setState({
            id: '',
            result: '',
            description: '',
            definition: ''
        })
    })
}

Let's talk about what this function is doing. It's taking an event, in this case, the click of submitting in a form and preventing default, which in this case is the page reloading. Then we're doing fetch with a method of post.

Notice that in our body, we're using our state. This works because this is the format we've told the workout log back end to accept.

Also notice the "Authorization" field in our header, where we're passing our token from props. Remember that this token comes from our parent components, and we've just passed it down several times as a prop so that we can use it to make API requests.

Then we call .json() on the response, like normal, and then we're calling a function from our parent component called updateWorkoutsArray() which we know just calls a GET to get all the workouts.

Lastly, we're setting our state back to nothing, so that when we want to create a new workout, we'll be starting with a blank slate.

Step 4. Setting Up the Render

We want to create a form to capture user input and also call our submit function. We're going to be using ReactStrap for a lot of the things in our render. Type the following code then we'll discuss it:

render() {
    return (
        <div>
            <h3>Log a Workout</h3>
            <hr />
            <Form onSubmit={this.handleSubmit} >
                <FormGroup>
                    <Label for="result">Result</Label>
                    <Input id="result" type="text" name="result" value={this.state.result} placeholder="enter result" onChange={this.handleChange} />
                </FormGroup>
                <FormGroup>
                    <Label for="def">Type</Label>
                    <Input type="select" name="definition" id="definition" value={this.state.definition} onChange={this.handleChange} placeholder="Type">
                        <option></option>
                        <option value="Time">Time</option>
                        <option value="Weight">Weight</option>
                        <option value="Distance">Distance</option>
                    </Input>
                </FormGroup>
                <FormGroup>
                    <Label for="description">Notes</Label>
                    <Input id="description" type="text" name="description" value={this.state.description} placeholder="enter description" onChange={this.handleChange} />
                </FormGroup>
                <Button type="submit" color="primary"> Submit </Button>
            </Form>
        </div>
    )
}

Things to note here:

  • <Form>, <Input>, <Button>, <FormGroup>, etc. are all from reactstrap

  • Notice how the input fields all have onChange set to this.handleChange, so that when a user types, this function is called and the state is updated. You can see this in action if you open up your React Dev Tools in your browser

  • Notice how on form onSubmit we're calling our handleSubmit function

  • Notice how the name on the inputs matches that in the state

Finished Code

import React, { Component } from 'react';
import { Button, Form, FormGroup, Label, Input } from 'reactstrap';


class WorkoutCreate extends Component {

    constructor(props) {
        super(props)
        this.state = {
            id: '',
            result: '',
            description: '',
            definition: ''
        };
    }

    handleChange = (event) => {
        // try console.log(event) to see when this method will be invoked
        this.setState({
            [event.target.name]: event.target.value
        })
    }

    handleSubmit = (event) => {
        event.preventDefault();
        fetch(`http://localhost:3000/api/log/`, {
            method: 'POST',
            body: JSON.stringify({ log: this.state }),
            headers: new Headers({
                'Content-Type': 'application/json',
                'Authorization': this.props.token
            })
        })
            .then((res) => res.json())
            .then((logData) => {
                // after we create a log we want to pull that data from the server.
                this.props.updateWorkoutsArray()
                this.setState({
                    id: '',
                    result: '',
                    description: '',
                    definition: ''
                })
            })
    }

    render() {
        return (
            <div>
                <h3>Log a Workout</h3>
                <hr />
                {/* after the form is submitted the data gets sent to the method above*/}
                <Form onSubmit={this.handleSubmit} >
                    <FormGroup>
                        <Label for="result">Result</Label>
                        <Input id="result" type="text" name="result" value={this.state.result} placeholder="enter result" onChange={this.handleChange} />
                    </FormGroup>
                    <FormGroup>
                        <Label for="def">Type</Label>
                        <Input type="select" name="definition" id="definition" value={this.state.definition} onChange={this.handleChange} placeholder="Type">
                            <option></option>
                            <option value="Time">Time</option>
                            <option value="Weight">Weight</option>
                            <option value="Distance">Distance</option>
                        </Input>
                    </FormGroup>
                    <FormGroup>
                        <Label for="description">Notes</Label>
                        <Input id="description" type="text" name="description" value={this.state.description} placeholder="enter description" onChange={this.handleChange} />
                    </FormGroup>
                    <Button type="submit" color="primary"> Submit </Button>
                </Form>
            </div>
        )
    }
}

export default WorkoutCreate;

Let's include this in the WorkoutIndex Component import section

import WorkoutCreate from './WorkoutCreate';

then in the render let's add the component in lieu of the comment

<Col md="3">
    <WorkoutCreate token={this.props.token} updateWorkoutsArray={this.fetchWorkouts}/>
</Col>

Next, we'll look at Workout Table

Last updated