Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React event handlers with Typescript and JSX

I'm having problems updating state in a React component I'm writing in TypeScript (React with Addons 0.13.3, Typescript 1.6.0-dev.20150804, definition file from http://definitelytyped.org/).

/// <reference path="react/react-addons.d.ts" />
import React = require("react/addons");

interface AppState {
}

interface TestState {
    liked: boolean,
    name: string
}

class Tester extends React.Component<any, TestState> {
    constructor(props) {
        super(props);
        this.state = { liked: false, name: "Anders" };
    }

    handleClick(evt, domNode): void {
        this.setState({ liked: !this.state.liked, name: this.state.name });
    }

    handleChange(evt, a, b, c): void {
        this.setState({ liked: this.state.liked, name: evt.target.value });
    }

    render() {
        var text = this.state.liked ? "liked " : "haven't liked "
        return (<div>You {text} {this.state.name} 
            <button onClick={this.handleClick}>Like</button>
            <input value={this.state.name} onChange={this.handleChange} />
        </div>);
    }
}

class App extends React.Component<{}, AppState> {
    constructor(props) {
        super(props);
    }

    render() {
        return (<div>
            <Tester />
        </div>);
    }
}

function Factory(props: {}) {
  return React.createElement(App, props);
}

export = Factory;

The calling code is

/// <reference path="react/react-addons.d.ts" />
import React = require("react/addons");
import App = require("app");

React.render(App({}), document.getElementById("jsapp"));

The component renders as I would expect but handleClick and handleChange methods don't update the state correctly. If I put breakpoints in those two methods and render then I see the following values for this:

  • render: this is a Tester object (what I would expect).
  • handleChange: this is a ReactClass.createClass.Constructor.
  • handleClick: this is a reference to the Window object.

The latter two mean that the state object isn't available.

Any suggestions gratefully received.

like image 338
Ian G Avatar asked Aug 12 '15 16:08

Ian G


People also ask

Can I use TypeScript in JSX?

TypeScript supports embedding, type checking, and compiling JSX directly to JavaScript.

How do I use onClick event in TypeScript?

To type the onClick event of an element in React, set its type to React. MouseEvent<HTMLElement> . The MouseEvent interface is used to type onClick events in React.


3 Answers

You have to bind methods with this because you don't use React.createClass that does it automatically. Example with a class syntax:

class Counter extends React.Component {
  constructor() {
    super();
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }
  handleChange() {
    ...
  }
  handleClick() {
    ...
  }
}
like image 200
shadeglare Avatar answered Oct 07 '22 09:10

shadeglare


You must change your render method:

render() {
    // ...
        <button onClick={this.handleClick.bind(this)}>Like</button>
        <input value={this.state.name} onChange={this.handleChange.bind(this)} />
    // ...
}

Since you're calling an event, the this keyword will be changed to the event's default context. By using .bind(this) you ensure that the context being called will be the instance of your class.

like image 40
Buzinas Avatar answered Oct 07 '22 08:10

Buzinas


Another method is to use fat arrow functions for event handlers which get automatic "this" binding.

 handleClick = (evt, domNode):void => {
     this.setState({ liked: !this.state.liked, name: this.state.name });
 };

 <button onClick={() => this.handleClick()}>Like</button>
like image 41
Sam Sippe Avatar answered Oct 07 '22 07:10

Sam Sippe