# Convert React Component to Ignition Module

**URL:** <https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942>\
**Category:** Module Development\
**Tags:** perspective\
**Created:** [January 23, 2023, 10:02pm UTC](https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942 "2023-01-23T22:02:40Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [January 23, 2023, 10:02pm UTC](https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942/1 "2023-01-23T22:02:40Z")

</div>

My Perspective module is now setup in my development environment in IntelliJ. I found it easier to take the example perspective component project and change all the packages and file names, than it was to take a skeleton project and try to build it up. see this post [https://forum.inductiveautomation.com/t/generate-skeleton-project-for-perspective-component/68829/5](https://forum.inductiveautomation.com/t/generate-skeleton-project-for-perspective-component/68829/5)

Now I am trying to figure out how to convert a react component I made using static files and make a Perspective component out of it. This is my react component

```auto
'use strict';

const e = React.createElement;

class KeyCapture extends React.Component {
  constructor(props) {
    super(props);
    this.state = { textInput: "" };

    this.handleKeyDown = this.handleKeyDown.bind(this);
  }

  handleKeyDown(e){
    e.preventDefault();

    if(/^[-~]$/.test(e.key)
      || e.key == " "
    ){
      e.stopPropagation();
      this.setState(prevState => ({
        textInput: prevState.textInput+e.key
      }));
    }
    else if(e.key == "Enter"){
      e.stopPropagation();
      this.setState({
        textInput: ""
      });
    }
  }

  render() {
    return this.state.textInput;
  }

  componentDidMount(){
    window.addEventListener("keydown", this.handleKeyDown);
  }

  componentWillUnmount(){
    window.removeEventListener("keydown", this.handleKeyDown);
  }
 }

const domContainer = document.querySelector('#key_capture_container');
const root = ReactDOM.createRoot(domContainer);
root.render(e(KeyCapture));

```

From the examples in the SDK I do not see how you can register a handlKeyDown(e) event. Also it seems like the syntax is a little different from what I was able to write from the react website to what I am seeing in the .tsx file. Is there some documentation I can be pointed to that would show me how to bind to certain javascript events perspective/.tsx file?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [January 23, 2023, 10:58pm UTC](https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942/2 "2023-01-23T22:58:41Z")

</div>

You should still be able to register event listeners in `componentDidMount` and `componentWillUnmount`. `window` is a global variable in browser based JS, so it's always available.  
You don't manually create an instance of your component (Perspective will do that for you), and you use the Perspective React component as the base class, but the rest of the code should be exactly the same.

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [January 23, 2023, 11:23pm UTC](https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942/3 "2023-01-23T23:23:02Z")

</div>

Thanks. It did not seem to like constructor(props) so changed how the state is initialized based on the TagCounter example. And then since I did not have the constructor anymore I followed the way the binding is done in the Messenger example with the @bind.

Here is what I have so far that works. it prints to the console in the designer!

```auto
export class KeyCapture extends Component<ComponentProps<KeyCaptureProps>, any> {
    state: KeyCaptureState = {
        textInput: ""
    };

    @bind
    handleKeyDown(e: KeyboardEvent): void {
        console.log("key down", e.key);
    }
    componentDidMount() {
        window.addEventListener("keydown", this.handleKeyDown);
    }
    componentWillUnmount() {
        window.removeEventListener("keydown", this.handleKeyDown);
    }

    render() {
        //const { props: { text }, emit } = this.props;
        const { emit } = this.props;

        return (
            <div
                {...emit()}
            >{this.state.textInput}
            </div>
        );
    }
}

```

Now when I add the rest of my code in the handleKeyDown I am getting an error

> TS7006: Parameter 'prevState' implicitly has an 'any' type

```auto
export class KeyCapture extends Component<ComponentProps<KeyCaptureProps>, any> {
    state: KeyCaptureState = {
        textInput: ""
    };

    @bind
    handleKeyDown(e: KeyboardEvent): void {
        e.preventDefault();

        if(/^[-~]$/.test(e.key)
            || e.key == " "
        ){
            e.stopPropagation();
            this.setState(prevState => ({
                textInput: prevState.textInput+e.key
            }));
        }
        else if(e.key == "Enter"){
            e.stopPropagation();
            this.setState({
                textInput: ""
            });
        }
    }
    componentDidMount() {
        window.addEventListener("keydown", this.handleKeyDown);
    }
    componentWillUnmount() {
        window.removeEventListener("keydown", this.handleKeyDown);
    }

    render() {
        //const { props: { text }, emit } = this.props;
        const { emit } = this.props;

        return (
            <div
                {...emit()}
            >{this.state.textInput}
            </div>
        );
    }
}

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [January 23, 2023, 11:30pm UTC](https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942/4 "2023-01-23T23:30:06Z")

</div>

Add types to your arrow function?

```typescript
            this.setState(prevState: KeyCaptureState => ({
                textInput: prevState.textInput+e.key
            }));

```

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [January 23, 2023, 11:31pm UTC](https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942/5 "2023-01-23T23:31:46Z")

</div>

Then I get

> TS2304: Cannot find name 'prevState'

---

<div class="post-metadata">

**Author:** ![Jonathan](https://avatars.discourse-cdn.com/v4/letter/j/aeb1de/32.png) [@Jonathan](https://forum.inductiveautomation.com/u/Jonathan)\
**Post date:** [January 23, 2023, 11:34pm UTC](https://forum.inductiveautomation.com/t/convert-react-component-to-ignition-module/68942/6 "2023-01-23T23:34:19Z")

</div>

Looks like I need () around the parameters. Now it compiles.

```auto
this.setState((prevState: KeyCaptureState) => ({
    textInput: prevState.textInput+e.key
}));

```
