How to Map Data to a Form in React
Welcome back to the course. In the last video, we set it up so we can have a newsletter to edit in our newsletter new form. Let's make it so we can get the data into our form. We just need to throw it into these inputs.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

So what I want to do is throw in these pieces of data into our fields in newsletterNewForm.js

newsletterNewForm.js

import React, { Component } from "react";
import { reduxForm, Field } from "redux-form";

import { FormTitle } from "../formTitle";
import { FormInput, FormButton, FormTextArea, FormImage } from "../formFields";

class NewNewsletterForm extends Component {
  render() {

    const { handleSubmit, formTitle, newsletterToEdit } = this.props;

    const { title, body, imageUrl } = newsletterToEdit;

    return (
      <form onSubmit={handleSubmit} className="new-newsletter-form">
        <FormTitle className="new-newsletter-form__title" text={formTitle} />
        <Field
          className="new-newsletter-form__newsletter-title"
          placeholder="Newsletter Title"
          name="title"
          type="text"
          title="Newsletter Title"
          component={FormInput}
          value={title}
        />  
        <Field
          className="new-newsletter-form__body"
          placeholder="Newsletter Body"
          name="body"
          type="text"
          title="Body"
          component={FormTextArea}
          value={body}
        />
        <Field
          className="new-newsletter-form__submit"
          small={true}
          danger={true}
          name="submit"
          type="submit"
          title="Submit"
          component={FormButton}
        />  
        <Field
          className="new-newsletter-form__cancel"
          small={true}
          name="cancel"
          type="button"
          title="Cancel"
          component={FormButton}
          onClick={this.props.onCancel}
        />  
        <Field
          className="new-newsletter-form__image"
          small={true}
          name="image"
          type="file"
          title="Image"
          component={FormImage}
          src={imageUrl}
        />  


      </form>
    );
  }
}

NewNewsletterForm = reduxForm({
  form: "newnewsletter"
})(NewNewsletterForm);

export default NewNewsletterForm;

Let's hop into formFields.js and add in our values. We're going to set up a ternary expression, because this data won't always be here, like when we create a new newsletter. We'll start with title.

formFields.js

export class FormInput extends Component {
    render() {
        const { className, title, input, type, placeholder, value } = this.props;
        return (
            <div className={`${className} form-input`}>
                <label className='form-input__title'>{title}</label>
                <input
                    className='form-input__input'
                    type={type}
                    {...input}
                    placeholder={placeholder}    
                    value={value ? value : ''}
                />
            </div>
        )
    }
}

Basically it's asking if the title exists as value, and if it does it will render that data, otherwise, it will just put an empty string. Let's go to the text area and do the same thing.

formFields.js

export class FormTextArea extends Component {
    render() {
        const { className, title, input, type, placeholder } = this.props;
        return (
            <div className={`${className} form-textarea`}>
                <label className='form-textarea__title'>{title}</label>
                <textarea
                    className='form-textarea__input'
                    type={type}
                    {...input}
                    placeholder={placeholder} 
                    value={value ? value : ''}
                >

                </textarea>
            </div>
        )
    }
}

Image is ok by itself since it's already set up. Now, if we were to go to the browser to try this out, we would encounter an error that won't let us log in. This is happening because we directly messed with our form for inputs by telling it to throw in an empty string if no data is present. We need to set this up so that it won't affect the rest of our inputs.

We could build a separate form input just for our edit, but we don't need to do that, neither do we need to build a whole separate component just for this. What we need to do is just say if it doesn't exist then we don't return an empty string. We want to return a value, so we'll say input.value and see if that works.

formFields.js

export class FormInput extends Component {
    render() {
        const { className, title, input, type, placeholder, value } = this.props;
        return (
            <div className={`${className} form-input`}>
                <label className='form-input__title'>{title}</label>
                <input
                    className='form-input__input'
                    type={type}
                    {...input}
                    placeholder={placeholder}    
                    value={value ? value : input.value}
                />
            </div>
        )
    }
}

And it works. So the reason that works is that we already have an input. Now inside of our form input, we already have a ...input So we might be able to put that below and it would work, even if we change it back to an empty string since input has a value, so it will override it.

large

Now this might be a problem when we get to our newsletter edit because we going to put value in and then it'll override it with a blank value. So let's go try that. Click on edit and you'll see it's value is not defined even though we checked it.

large

So it's saying value is not defined for some reason. Let's check where that's occurring. In our error message, it says that it's occuring in formFields.js on line 34, which means that it's down here in form text area. And that's because we didn't pass in value, so let's do that.

formFields.js

export class FormTextArea extends Component {
    render() {
        const { className, title, input, type, placeholder, value } = this.props;
        return (
            <div className={`${className} form-textarea`}>
                <label className='form-textarea__title'>{title}</label>
                <textarea
                    className='form-textarea__input'
                    type={type}
                    {...input}
                    placeholder={placeholder} 
                    value={value ? value : ''}
                >

                </textarea>
            </div>
        )
    }
}

And now it should work. Let's check our edit page. Nope, still not working. We are getting our values through redux, but it's not going into our forms. So let's go to newsletterNewForm.js and we're providing it as a prop which means we're probably trying to override something that already exists. What's really happening is we're trying to overwrite a prop that is already called value.

So let's just call these editValue and see what happens. We have to change this in formFields.js and newsletterNewForm.js.

formFields.js

export class FormInput extends Component {
    render() {
        const { className, title, input, type, placeholder, editValue } = this.props;
        return (
            <div className={`${className} form-input`}>
                <label className='form-input__title'>{title}</label>
                <input
                    className='form-input__input'
                    type={type}
                    {...input}
                    placeholder={placeholder}
                    value={editValue ? editValue : input.value}    
                />
            </div>
        )
    }
}

export class FormTextArea extends Component {
    render() {
        const { className, title, input, type, placeholder, editValue } = this.props;
        return (
            <div className={`${className} form-textarea`}>
                <label className='form-textarea__title'>{title}</label>
                <textarea
                    className='form-textarea__input'
                    type={type}
                    {...input}
                    placeholder={placeholder} 
                    value={editValue ? editValue : ''}   
                >

                </textarea>
            </div>
        )
    }
}

newsletterNewForm.js

        <Field
          className="new-newsletter-form__newsletter-title"
          placeholder="Newsletter Title"
          name="title"
          type="text"
          title="Newsletter Title"
          component={FormInput}
          editValue={title}
        />  
        <Field
          className="new-newsletter-form__body"
          placeholder="Newsletter Body"
          name="body"
          type="text"
          title="Body"
          component={FormTextArea}
          editValue={body}
        />

Let's check that out in the browser.

large

As you can see it works, so that's great. So that was obviously what was going on. It was trying to see value but likely in redux there is a property called value that was overriding whatever we are putting in value. We can log in, view the newsletter, and then go into edit.

It may have broken our new newsletter component, though. You see we have an error saying that title is undefined.

large

So in our newsletter new form it's saying title is undefined in our newsletterNewForm.js. So basically what's happening is it's trying to get our data and throw them in, but they don't even exist, so we can quickly fix this by just doing exactly what we did in our formFields.js and putting these ternary expressions in.

newsletterNewForm.js

        <Field
          className="new-newsletter-form__newsletter-title"
          placeholder="Newsletter Title"
          name="title"
          type="text"
          title="Newsletter Title"
          component={FormInput}
          editValue={title ? title : null}
        />  
        <Field
          className="new-newsletter-form__body"
          placeholder="Newsletter Body"
          name="body"
          type="text"
          title="Body"
          component={FormTextArea}
          editValue={body ? body : null}
        />

Now let's try it out to make sure it is working. And we get an error. So we need to do is set it up so that it will only pass in those values if newsletterToEdit exists. so we need to put this in a conditional up above. We'll have to create new variables because our scope will be changed.

newsletterNewForm.js

render() {

    const { handleSubmit, formTitle, newsletterToEdit } = this.props;

    var title = null;
    var body = null;
    var imageUrl = null;
    if(newsletterToEdit) {
      title = newsletterToEdit.title;
      body = newsletterToEdit.body;
      imageUrl = newsletterToEdit.imageUrl;
    }

Okay so pretty simple. It's just going to check if they exist. So let's save that let's go check it out now. Should be working.

And it works. Our edit form has all of our text in it, while the new form is empty. But we also need to show the image when we edit because you'll see it's not showing the image. So what we need to do is go to our form image and see what's going on.

It looks like we'll need to change the form input to say imageUrl, instead of src.

newsletterNewForm.js

        <Field
          className="new-newsletter-form__image"
          small={true}
          name="image"
          type="file"
          title="Image"
          component={FormImage}
          imageUrl={imageUrl}
        />  

So yeah that's how you edit it. Let's go ahead and get rid of some of the console.logs. You definitely just don't want console logs randomly in your application. You also don't want them in production especially if you're printing out data, because you don't want to just print out random data because that's a bad experience and it could be user data that you don't want vulnerable.

Let's commit our code and move on.

git status
git add .
git commit -m "mapped newsletter edit data into form"

I'll see in the next video.

Resources

Code at this stage