Search code examples
javascriptreactjstypescriptreact-typescript

React + TypeScript: Uncaught TypeError: this is undefined


I'm quite new to both React and TypeScript and I'm trying to build a stateful component to calculate weights and percentages for making bread dough.

I followed an example for a stateful component from the homepage of https://reactjs.org/.

When I change the value of the target-weight input I get the exception on the following line:

updateTargetWeight(e: React.SyntheticEvent): void {
    this.setState({targetWeight: this.getInputValue(e)}); // ERROR
    this.calculate();
}

Uncaught TypeError: this is undefined

When I use an anonymous function like this, it works fine.

onInput={(e: React.SyntheticEvent) => {
    this.setState({hydrationPercentage: this.getInputValue(e)});
    this.calculate();
}}

Am I overlooking something, or don't understand something right?

export interface DoughFormProps {
    hydrationPercentage: number,
    yeastPercentage: number,
    saltPercentage: number,
    targetWeight: number,
    flourWeight: number,
    waterWeight: number,
    yeastWeight: number,
    saltWeight: number,
}

class DoughCalculatorForm extends React.Component<{}, DoughFormProps> {

    constructor(props: DoughFormProps) {

        super(props);

        this.state = {
            hydrationPercentage: 68,
            yeastPercentage: 1,
            saltPercentage: 2,
            targetWeight: 600,
            flourWeight: 0,
            waterWeight: 0,
            yeastWeight: 0,
            saltWeight: 0
        };
    }

    getInputValue(e: React.SyntheticEvent): number {
        const target = e.target as HTMLInputElement;
        return parseInt(target.value);
    }

    updateTargetWeight(e: React.SyntheticEvent): void {
        this.setState({targetWeight: this.getInputValue(e)});
        this.calculate();
    }

    calculate() {}

    render() {
        return (
            <form>
                <div className="input-container">
                    <label>
                        Target weight
                        <input type="number"
                               name="target-weight"
                               value={this.state.targetWeight}
                               onInput={this.updateTargetWeight}
                        />
                    </label>
                </div>
                <div className="input-container">
                    <label>
                        Hydration percentage
                        <input type="number"
                               name="hydration-percentage"
                               value={this.state.hydrationPercentage}
                               onInput={(e: React.SyntheticEvent) => {
                                   this.setState({hydrationPercentage: this.getInputValue(e)});
                                   this.calculate();
                               }}
                        />
                    </label>
                </div>
            </form>
        );
    }
}

export default DoughCalculatorForm;

Solution

  • constructor(){
    
        this.updateTargetWeight = this.updateTargetWeigth.bind(this); <---- THIS
    
    }
    
    updateTargetWeight(e: React.SyntheticEvent): void {
        this.setState({targetWeight: this.getInputValue(e)});
        this.calculate();
    }
    

    when you define your function as NOT an arrow function you generally need to bind it*

    (* = there are some plugins (webpack/babel i dont remember) which automatically do this for you but you need to configure those)

    other way would be to define it as arrow function, when you defined your function inline it worked because it was an arrow function.

    updateTargetWeight = (e: React.SyntheticEvent): void => {
        this.setState({targetWeight: this.getInputValue(e)});
        this.calculate();
    }