0

My component EditProfile makes use of a reusable form component ProfileForm as shown below.

EditProfile:

class EditProfile extends Component {
    constructor(props) {
        super(props);

        this.onChange = this.onChange.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
    }

    componentDidMount() {
      this.props.getCurrentProfile();
    }

    onSubmit(profileData) {
      this.props.createProfile(profileData, this.props.history);
    }

    onChange(evt) {
      this.setState({ [evt.target.name]: evt.target.value });
    }

    render() {
      return <ProfileForm profile={this.props.profile} errors={this.props.errors} onSubmit={this.onSubmit} />
    }

    EditProfile.propTypes = {
      profile: PropTypes.object.isRequired,
      errors: PropTypes.object.isRequired,
      createProfile: PropTypes.func.isRequired,
      getCurrentProfile: PropTypes.func.isRequired
    };

    const mapStateToProps = (state) => ({
      profile: state.profile,
      errors: state.errors
    });

    export default connect(mapStateToProps, { createProfile, getCurrentProfile })(withRouter(EditProfile));
}

ProfileForm:

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import isEmpty from '../validation/is-empty';
import PropTypes from 'prop-types';
import TextFieldGroup from './common/TextFieldGroup';
import InputGroup from './common/InputGroup';
import SelectListGroup from './common/SelectListGroup';
import TextAreaFieldGroup from './common/TextAreaFieldGroup';

class ProfileForm extends Component {
    constructor(props) {
        super(props);

        this.state = {
            displaySocialInputs: false,
            handle: props.profile ? props.profile.handle : '',
            company: props.profile ? props.profile.company : '',
            website: props.profile ? props.profile.website : '',
            location: props.profile ? props.profile.location : '',
            status: props.profile ? props.profile.status : '',
            skills: props.profile ? props.profile.skills : '',
            githubusername: props.profile ? props.profile.githubusername : '',
            bio: props.profile ? props.profile.bio : '',
            twitter: props.profile ? props.profile.twitter : '',
            facebook: props.profile ? props.profile.facebook : '',
            linkedin: props.profile ? props.profile.linkedin : '',
            youtube: props.profile ? props.profile.youtube : '',
            instagram: props.profile ? props.profile.instagram : '',
            errors: props.errors || {}
        };

        this.onChange = this.onChange.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.errors) {
            this.setState({ errors: nextProps.errors });
        }

        if (nextProps.profile.profile) {
            const profile = nextProps.profile.profile;

            // bring skills array back to csv
            const skillsCsv = profile.skills.join(',');

            // if profile field wasn't provided set it to an empty string
            profile.company = !isEmpty(profile.company) ? profile.company : '';
            profile.website = !isEmpty(profile.website) ? profile.website : '';
            profile.location = !isEmpty(profile.location) ? profile.location : '';
            profile.githubusername = !isEmpty(profile.githubusername) ? profile.githubusername : '';
            profile.bio = !isEmpty(profile.bio) ? profile.bio : '';
            profile.social = !isEmpty(profile.social) ? profile.social : {};
            profile.twitter = !isEmpty(profile.social.twitter) ? profile.social.twitter : '';
            profile.facebook = !isEmpty(profile.social.facebook) ? profile.social.facebook : '';
            profile.linkedin = !isEmpty(profile.social.linkedin) ? profile.social.linkedin : '';
            profile.youtube = !isEmpty(profile.social.youtube) ? profile.social.youtube : '';
            profile.instagram = !isEmpty(profile.social.instagram) ? profile.social.instagram : '';

            // set component state
            this.setState({
                handle: profile.handle,
                company: profile.company,
                website: profile.website,
                location: profile.location,
                status: profile.status,
                skills: skillsCsv,
                githubusername: profile.githubusername,
                bio: profile.bio,
                twitter: profile.twitter,
                facebook: profile.facebook,
                linkedin: profile.linkedin,
                youtube: profile.youtube,
                instagram: profile.instagram
            });
        }
    }

    onChange(evt) {
        this.setState({ [evt.target.name]: evt.target.value });
    }

    onSubmit(evt) {
        evt.preventDefault();

        const profileData = {
            handle: this.state.handle,
            company: this.state.company,
            website: this.state.website,
            location: this.state.location,
            status: this.state.status,
            skills: this.state.skills,
            githubusername: this.state.githubusername,
            bio: this.state.bio,
            twitter: this.state.twitter,
            facebook: this.state.facebook,
            linkedin: this.state.linkedin,
            youtube: this.state.youtube,
            instagram: this.state.instagram
        }

        this.props.onSubmit(profileData);
    }

    render() {
        const { errors, displaySocialInputs } = this.state;

        let socialInputs;
        if (displaySocialInputs) {
            socialInputs = (
                <div>
                    <InputGroup
                        placeholder="Twitter profile URL"
                        name="twitter"
                        icon="fab fa-twitter"
                        value={this.state.twitter}
                        onChange={this.onChange}
                        error={errors.twitter}
                    />
                    <InputGroup
                        placeholder="Facebook profile URL"
                        name="facebook"
                        icon="fab fa-facebook"
                        value={this.state.facebook}
                        onChange={this.onChange}
                        error={errors.facebook}
                    />
                    <InputGroup
                        placeholder="LinkedIn profile URL"
                        name="linkedin"
                        icon="fab fa-linkedin"
                        value={this.state.linkedin}
                        onChange={this.onChange}
                        error={errors.linkedin}
                    />
                    <InputGroup
                        placeholder="YouTube channel URL"
                        name="youtube"
                        icon="fab fa-youtube"
                        value={this.state.youtube}
                        onChange={this.onChange}
                        error={errors.youtube}
                    />
                    <InputGroup
                        placeholder="Instagram page URL"
                        name="instagram"
                        icon="fab fa-instagram"
                        value={this.state.instagram}
                        onChange={this.onChange}
                        error={errors.instagram}
                    />
                </div>
            )
        }

        // select options for status
        const options = [
            { label: '* Select professional status', value: '', disabled: 'disabled'},
            { label: 'developer', value: 'developer' },
            { label: 'senior developer', value: 'senior developer' },
            { label: 'manager', value: 'manager' },
            { label: 'student or learning', value: 'student or learning' },
            { label: 'instructor or teacher', value: 'instructor or teacher' },
            { label: 'intern', value: 'intern' },
            { label: 'other', value: 'other' }
        ];

        return (
            <div className="create-profile">
                <div className="container">
                    <div className="row">
                        <div className="col-md-8 m-auto">
                            {this.state.handle && <Link to="/dashboard" className="btn btn-light">Go to dashboard</Link>}
                            <h1 className="display-4 text-center">Edit your profile</h1>
                            {!this.state.handle && <p className="lead text-center">Lets get some information to make your profile stand out</p>}
                            <small className="d-block pb-3">* = required fields</small>
                            <form onSubmit={this.onSubmit}>
                                <TextFieldGroup
                                    placeholder="* Profile Handle"
                                    name="handle"
                                    value={this.state.handle}
                                    onChange={this.onChange}
                                    error={errors.handle}
                                    info="A unique handle for your profile URL. Your full name, company name, nickname, etc."
                                />
                                <SelectListGroup
                                    placeholder="Status"
                                    name="status"
                                    value={this.state.status}
                                    onChange={this.onChange}
                                    options={options}
                                    error={errors.status}
                                    info="Give us an idea where you are at in your career"
                                />
                                <TextFieldGroup
                                    placeholder="Company"
                                    name="company"
                                    value={this.state.company}
                                    onChange={this.onChange}
                                    error={errors.company}
                                    info="Could be your own company or one you work for"
                                />
                                <TextFieldGroup
                                    placeholder="Website"
                                    name="website"
                                    value={this.state.website}
                                    onChange={this.onChange}
                                    error={errors.website}
                                    info="Could be your own website or a company one"
                                />
                                <TextFieldGroup
                                    placeholder="Location"
                                    name="location"
                                    value={this.state.location}
                                    onChange={this.onChange}
                                    error={errors.location}
                                    info="City or city & state suggested (e.g. Boston, MA)"
                                />
                                <TextFieldGroup
                                    placeholder="Skills"
                                    name="skills"
                                    value={this.state.skills}
                                    onChange={this.onChange}
                                    error={errors.skills}
                                    info="Please use comma separated values (e.g. HTML, CSS, JS, PHP)"
                                />
                                <TextFieldGroup
                                    placeholder="Github username"
                                    name="githubusername"
                                    value={this.state.githubusername}
                                    onChange={this.onChange}
                                    error={errors.githubusername}
                                    info="If you want your latest repos and a Github link, include your username"
                                />
                                <TextAreaFieldGroup
                                    placeholder="Short bio"
                                    name="bio"
                                    value={this.state.bio}
                                    onChange={this.onChange}
                                    error={errors.bio}
                                    info="Tell us a little about yourself"
                                />
                                <div className="mb-3">
                                    <button type="button"
                                        onClick={() => this.setState((prevState) => ({ displaySocialInputs: !prevState.displaySocialInputs }))}
                                        className="btn btn-light">Add social network links
                                    </button>
                                    <span className="text-muted">Optional</span>
                                </div>
                                {socialInputs}
                                <input type="submit" value="Submit" className="btn btn-info btn-block mt-4" />
                            </form>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

ProfileForm.propTypes = {
    profile: PropTypes.object.isRequired,
    errors: PropTypes.object.isRequired
};

export default ProfileForm;

I believed the value fields on TextFieldGroup, SelectListGroup, TextAreaFieldGroup to be set to '' in the ProfileForm constructor but get warnings similar to

Failed prop type: The prop `value` is marked as required in `TextAreaFieldGroup`, but its value is `undefined`.

and the same warning for the other input components. Eventually get this one

A component is changing an uncontrolled input of type text to be controlled

Where am I wrong?

El Anonimo
  • 1,759
  • 3
  • 24
  • 38

2 Answers2

0

The prop you are getting in EditProfile is not declared in the

  this.state = {

        }

of parent component. This has already being answered before React - changing an uncontrolled input.

Look into your parent component.This error is shown when you do not set the state of an object an use it as value in input or select tags.

Deepak Adhana
  • 104
  • 1
  • 8
  • Did you mean adding `this.state = { profile: {}, errors: {}` to the `EditProfile` constructor? I don't believe that would help cause those props aren't the ones setting values on the input components in `ProfileForm`. I added them to the `EditProfile` constructor it didn't help. – El Anonimo Feb 04 '19 at 13:13
  • I meant that the EditProfile componet is getting profile as a props which you are passing down to the child component. You have to set the state in parent of `EditProfile` component .This profile is not set in parent component. – Deepak Adhana Feb 04 '19 at 13:17
  • Adding those props to the constructor didn't resolve the issue. Please see the comment above for more details. – El Anonimo Feb 04 '19 at 13:30
  • you have an uncontrolled input if it has a value of `undefined`. So I suppose that you pass a profile object as prop but it does not contains some keys or the value of some are `undefined`. So put a `|| ""` (or something else) somewhere to not have a value to undefined. – BenoitVasseur Feb 04 '19 at 21:09
  • @ElAnonimo what BenoitVasseur said is right. You have set some some value to the profile prop or place a null check before passing it to ProfileForm component. – Deepak Adhana Feb 05 '19 at 05:45
  • Setting the `ProfileForm` constructor fields to `''` with no check for `props` seemed to help. Though I don't yet see why. Passing a `null` value to `ProfileForm` wasn't supposed to cause the error cause its constructor checks for `null` values. Was it? – El Anonimo Feb 05 '19 at 08:04
  • i got the same error on multiple occasions in my project. What i found out was this that i was changing the value of input , without declaring the state. – Deepak Adhana Feb 05 '19 at 13:18
0

The reason was probably TextFieldGroup and the other similar components were initialized with value={this.state.<prop_name>} before the ProfileForm constructor got involved to setting the state on ProfileForm. The error only occurs when load the EditProfile component on page refresh. If I come there from a page with a user profile then no error.

El Anonimo
  • 1,759
  • 3
  • 24
  • 38