1

Can you explain why react state is visible in fuction handleFinishChange and cannot be seen in validationFinishTime. Both are transfer to component InputFieldForm. Execution of this code lead to Uncaught TypeError: Cannot read property 'finish' of undefined in validationFinishTime when the field is changed in input.

Component ShiftFormAdmin

import React from 'react'
import {browserHistory} from 'react-router'
import Loading from './../form/loading.jsx'
import message from './../../constants/message';
import style from './../../constants/style';
import ErrorInformation from '../form/ErrorInformationForm.jsx';
import InputFieldForm from '../form/InputFieldForm.jsx';
import SelectFieldForm from '../form/SelectFieldForm.jsx';
import urls from './../../constants/urls'
import moment from 'moment';

export default class ShiftFormAdmin extends React.Component{

constructor(props){
    super(props)
    this.state = {
        box : {value : '', isValid : true},
        washerMan : {value : "", isValid : true},
        finish : {value : this.props.finish, isValid : true},
        start : '',
        availableWasherManList : [],
        boxCount : this.props.initData.boxCount,
        errorDisplay : style.DISPLAY_NONE,
        errorMessage : message.MESSAGE_DEFAULT_ERROR,
        isWasherMansDownloaded: false,
        isCurrentTimeDownloaded : false
    }
    this.handleSubmit = ::this.handleSubmit
    this.handleBoxChange = ::this.handleBoxChange
    this.downloadWasherMans = ::this.downloadWasherMans
    this.filterWasherMan = ::this.filterWasherMan
    this.downloadServerTime = ::this.downloadServerTime
    this.setStartTime = :: this.setStartTime
    this.handleFinishChange = ::this.handleFinishChange
}

handleFinishChange(e){
    this.setState({
        finish : {
            value : e.target.value,
            isValid : true
        }
    })
}

validationFinishTime(value){
    let result = false;
    console.log(this.state.finish.value)
    return result;
}

render(){
    let data = <Loading/>
    let boxCount = [];
    for(let i=1; i<=this.state.boxCount; i++){
        boxCount.push(i);
    }

    if(this.state.isWasherMansDownloaded && this.state.isCurrentTimeDownloaded) {
        data =
            <form className="form-horizontal" onSubmit={this.handleSubmit} id="shiftForm">

                <InputFieldForm
                    title="finish:"
                    name="finish"
                    onChange={this.handleFinishChange}
                    maxLength={5}
                    defaultValue={this.state.finish.value}
                    validation={this.validationFinishTime}
                    errorMessage="error"/>

                <div className="form-group">
                    <div className="col-sm-offset-2 col-sm-10">
                        <button type="submit" className="btn btn-success"> ADD
                        </button>
                    </div>
                </div>

            </form>
    }
    return(
            <div className="form-general">
                {data}
            </div>
    )
}
}

Class FormInputField

export default class FormInputField extends React.Component{

constructor(props) {
    super(props);
    this.state = {
        value : this.props.defaultValue ? this.props.defaultValue  : '',
        errorDisplay : st.DISPLAY_NONE,
        errorMessage : msg.MESSAGE_DEFAULT_ERROR,
    }
    this.validation = this.validation.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
}

handleChange(e){
    let valid = this.validation(e.target.value.trim());
    if (this.props.onChange){
        this.props.onChange(e, valid)
    }
}

validation(value){
    let errorDisplay = st.DISPLAY_NONE;
    let errorMessage = '';
    let valid = true;

    if (this.props.notShow != true) {
        if (this.props.required && jQuery.isEmptyObject(value)) {
            errorMessage = msg.MESSAGE_FIELD_IS_REQUIRED;
            errorDisplay = st.DISPLAY_BLOCK;
            valid = false;
        } else if (this.props.maxLength && value.trim().length > this.props.maxLength) {
            valid = false;
            errorMessage = this.props.maxLength + msg.MESSAGE_MAX_LENGTH;
            errorDisplay = st.DISPLAY_BLOCK;
        } else if (this.props.validation && !this.props.validation(value.trim())) {
            valid = false;
            errorDisplay = st.DISPLAY_BLOCK;
            errorMessage = this.props.errorMessage;
        }
    }
    this.setState({
        value : value,
        errorDisplay : errorDisplay,
        errorMessage : errorMessage
    })

    return valid;
}

handleBlur(e) {
    this.validation(e.target.value.trim());
}

    render(){
        let data;
        let readonly = undefined;
        if (this.props.readonly){
            readonly = true
        }
        if(this.props.notShow != true){
            data =  <div className="form-group">
                <label htmlFor = {this.props.name} className="col-sm-2 control-label">{this.props.title}</label>
                <div className="col-sm-10">

                    <input name={this.props.name}
                           type="text" className="form-control"
                           id={this.props.name}
                           placeholder={this.props.placeholder}
                           value={this.state.value}
                           onChange={this.handleChange}
                           onBlur={this.handleBlur}
                           disabled={this.props.disabled}
                           readOnly = {readonly}
                    />
                </div>
            </div>
        }

    return(
        <div>
            {data}
        </div>
    )
}

}

Bizon4ik
  • 2,174
  • 4
  • 17
  • 38
  • Thanks for accepting my answer. Can you confirm that both suggestions worked? I just haven't seen bind syntax using `::` before :-) – Pineda Dec 04 '16 at 13:23
  • No worries, found an answer: [Proposed ES6 shortcut for bind](http://stackoverflow.com/q/31220078/2902660) – Pineda Dec 04 '16 at 13:25

1 Answers1

1

Suggested solution: Bind your event handler to the component context/scope:

(same as other statements in your constructor, but I haven't used before)

this.validationFinishTime = ::this.validationFinishTime

OR (the way I normally do this; again, within the constructor):

this.validationFinishTime = this.validationFinishTime.bind(this);

Reasoning behind suggestion:

This is a bit of a guess here because I haven't seen this syntax before (this.handleFinishChange = ::this.handleFinishChange) but I'm guessing that it binds the component's context of this to the method so the method this.handleFinishChange can reference things like this.setState and this.state.

validationFinishTime has no binding to the components context so when it references this.state.finish, there is no reference to this.state because the this refers to the context which it was called: which is the inline-event context of 'validation' on <InputFieldForm/>.

Pineda
  • 6,865
  • 3
  • 26
  • 39