25

From the draft-js documention, one can (in vanilla React, with no typescript) setup the Draft-js environment thus, noticing that the onChange property can be declared directly in the constructor:

import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState} from 'draft-js';

class MyEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {editorState: EditorState.createEmpty()};
    this.onChange = (editorState) => this.setState({editorState});
  }
  render() {
    const {editorState} = this.state;
    return <Editor editorState={editorState} onChange={this.onChange} />;
  }
}

However, when I try to do the same with Typescript/React (code below), I get this error

error TS2339: Property 'onChange' does not exist on type 'Main'.

class Main extends React.Component<MainProps, MainState> {

    constructor(props) {
    super(props);
    this.state = { todos: [], editorState: EditorState.createEmpty() };
    this.onChange = (editorState) => this.setState({ editorState });
  }

I also tried adding onChange as a property to the props

interface MainProps {
    model: Model;
    onChange: Function;
}

What is the appropriate way to declare such a function property in typescript/react?

4
  • Why do you want to do it this way instead of just have the onChange as a declared method of the class? Commented May 18, 2016 at 14:33
  • I tried declaring it as a method on the class (that's fine) but since the Draft-js docs showed a different example, I thought I'd try it, and wondered why it's not compiling with TypeScript. Commented May 18, 2016 at 14:38
  • Why don't you just declare the variable on Main? Commented May 18, 2016 at 14:40
  • @iberbeu this.propertyName seems to be the way to declare a property in the constructor (from what I've seen in docs) can you show me exactly what you mean if it's different Commented May 18, 2016 at 14:47

3 Answers 3

18

Try this:

class Main extends React.Component<MainProps, MainState> {
    constructor(props) {
        super(props);
        this.state = { todos: [], editorState: EditorState.createEmpty() };
        this.onChange = (editorState) => this.setState({ editorState });
    }

    onChange: (state: MainState) => void;

}

I haven't tested it, but I think it should work.


Edit

Yeah, there's a problem there that I haven't noticed, it should be:

class Main extends React.Component<MainProps, MainState> {
    constructor(props) {
        super(props);

        this.state = { todos: [], editorState: EditorState.createEmpty() };
        this.onChange = (editorState) => this.setState({
            editorState: editorState
        } as MainState);
    }

    onChange: (state: MainState) => void;

}

The type assertion (as MainState) is needed if the todos property isn't optional, if it is optional (todos?: any[]) then there's no need for the assertion.

As for what seems to be duplication with the onChange definition, it is explained in short in the Mixins part of the typescript docs, but in your example the definition in the class:

onChange: (state: MainState) => void;

let's the compiler know that instances of Main have this method called onChange that receives a MainState and returns void.
But the implementation of this method is only assigned when the instance is created in the ctor:

this.onChange = (editorState) => this.setState({ editorState });

If the definition is missing then the assignment in the ctor will produce a compilation error: property 'onChange' does not exist on type 'Main'.

Sign up to request clarification or add additional context in comments.

2 Comments

regarding the onChange in the constructor in your code, the compiler says MainState is not assignable to type EditorState, so I assume that this.setState({ editorState }); is attempting to implicitly assign MainState to the editorState property of the state object? Secondly, it's unclear to me why there's an onChange property in the constructor and also immediately following.
check my revised answer
3

You can use handleChange method like this :

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Editor, EditorState } from 'draft-js';

interface MyEditorProps {
}

class MyEditor extends React.Component<MyEditorProps, any> {
  constructor(props: MyEditorProps) {
    super(props);

    this.state = { editorState: EditorState.createEmpty() };
  }
  handleChange(e: EditorState) {
    this.setState({ editorState: e });
  }
  render() {
    return (
      <Editor editorState={this.state.editorState} onChange={e => this.handleChange(e)} />
    );
  }
}

ReactDOM.render(
  <MyEditor />,
  document.getElementById('editor'),
);

export { MyEditor }

Comments

1

Alternatively, you can try it like this:

private onChange = (editorState) => this.setState({ editorState } as MainState)

just in the body of the class, where you define other class properties. I dunno, which version you're running, but this code totally works in ES2015 and TypeScript 2.0.10

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.