Luis Valencia
Luis Valencia

Reputation: 33998

Cannot read property 'props' of undefined in form

I have the following react component, but I cant find the reason of the error above, help appreciated

import React, { Component } from 'react';
import { Input} from 'antd';
import Form from '../../components/uielements/form';
import Button from '../../components/uielements/button';
import Notification from '../../components/notification';
import { adalApiFetch } from '../../adalConfig';


const FormItem = Form.Item;

class CreateSiteCollectionForm extends Component {
    constructor(props) {
        super(props);
        this.state = {Alias:'',DisplayName:'', Description:''};
        this.handleChangeAlias = this.handleChangeAlias.bind(this);
        this.handleChangeDisplayName = this.handleChangeDisplayName.bind(this);
        this.handleChangeDescription = this.handleChangeDescription.bind(this);

    };

    handleChangeAlias(event){
        this.setState({Alias: event.target.value});
    }

    handleChangeDisplayName(event){
        this.setState({DisplayName: event.target.value});
    }

    handleChangeDescription(event){
        this.setState({Description: event.target.value});
    }

    handleSubmit(e){
        e.preventDefault();
        this.props.form.validateFieldsAndScroll((err, values) => {
            if (!err) {
                let data = new FormData();
                //Append files to form data
                data.append(JSON.stringify({"Alias": this.state.Alias,
                 "DisplayName": this.state.DisplayName, 
                 "Description": this.state.Description
                }));

                const options = {
                  method: 'post',
                  body: data,
                  config: {
                    headers: {
                      'Content-Type': 'multipart/form-data'
                    }
                  }
                };

                adalApiFetch(fetch, "/SiteCollections", options)
                  .then(response =>{
                    if(response.status === 204){
                        Notification(
                            'success',
                            'Site collection created',
                            ''
                            );
                     }else{
                        throw "error";
                     }
                  })
                  .catch(error => {
                    Notification(
                        'error',
                        'Site collection not created',
                        error
                        );
                    console.error(error);
                });
            }
        });      
    }

    render() {
        const { getFieldDecorator } = this.props.form;
        const formItemLayout = {
        labelCol: {
            xs: { span: 24 },
            sm: { span: 6 },
        },
        wrapperCol: {
            xs: { span: 24 },
            sm: { span: 14 },
        },
        };
        const tailFormItemLayout = {
        wrapperCol: {
            xs: {
            span: 24,
            offset: 0,
            },
            sm: {
            span: 14,
            offset: 6,
            },
        },
        };
        return (
            <Form onSubmit={this.handleSubmit}>
                <FormItem {...formItemLayout} label="Alias" hasFeedback>
                {getFieldDecorator('Alias', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your alias',
                        }
                    ]
                })(<Input name="alias" id="alias" onChange={this.handleChangeAlias} />)}
                </FormItem>
                <FormItem {...formItemLayout} label="Display Name" hasFeedback>
                {getFieldDecorator('displayname', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your display name',
                        }
                    ]
                })(<Input name="displayname" id="displayname" onChange={this.handleChangedisplayname} />)}
                </FormItem>
                <FormItem {...formItemLayout} label="Description" hasFeedback>
                {getFieldDecorator('description', {
                    rules: [
                        {
                            required: true,
                            message: 'Please input your description',
                        }
                    ],
                })(<Input name="description" id="description"  onChange={this.handleChangeDescription} />)}
                </FormItem>

                <FormItem {...tailFormItemLayout}>
                    <Button type="primary" htmlType="submit">
                        Create modern site
                    </Button>
                </FormItem>
            </Form>
        );
    }
}

const WrappedCreateSiteCollectionForm = Form.create()(CreateSiteCollectionForm);
export default WrappedCreateSiteCollectionForm;

Upvotes: 0

Views: 1150

Answers (3)

c-chavez
c-chavez

Reputation: 7496

Just use arrow functions for your handlers to keep the this context problem away.

According to MDN web docs:

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target.

So, in your components:

<Form onSubmit={(e) => this.handleSubmit(e)}>
...
onChange={(e) => this.handleChangeAlias(e)}
...
onChange={(e) => this.handleChangedisplayname(e)}
...
onChange={(e) => this.handleChangeDescription(e)}

And, don't bind in the constructor:

constructor(props) {
    super(props);
    this.state = {Alias:'',DisplayName:'', Description:''};
};

Problems with inline functions, and arrow functions

There are many articles on the subject, and I don't want to start a debate here, since it has already been debated for long.

If you are having problems of re-rendering with this solution, but think arrow functions are easier (to write, to read, to understand, to bind, etc) then have a look at Reflective-bind, which solves this problems in a very easy way.

These articles are important to have a good understanding on what inline functions and arrow functions do, and why you should use or not use them in your project:

Upvotes: 2

Sivcan Singh
Sivcan Singh

Reputation: 1892

Bind your handleSubmit in the constructor like so

 constructor(props) {
        super(props);
        this.state = {Alias:'',DisplayName:'', Description:''};
        this.handleChangeAlias = this.handleChangeAlias.bind(this);
        this.handleChangeDisplayName = this.handleChangeDisplayName.bind(this);
        this.handleChangeDescription = this.handleChangeDescription.bind(this);

        this.handleSubmit = this.handleSubmit.bind(this); // Bind it
    };

Upvotes: 0

andy mccullough
andy mccullough

Reputation: 9571

You need to bind handleSubmit to this

this.handleSubmit = this.handleSubmit.bind(this) in constructor

Upvotes: 1

Related Questions