How do I create elements using React in Theia?
To create a normal dialog, I created a class like this:
@injectable()
export class WizardDialog extends AbstractDialog<void> {
...
}
To put normal elements on the dialog, I append child elements to the contentNode like this:
constructor() {
this.contentNode.appendChild(this.createMainDivision());
}
private createMainDivision(): HTMLDivElement {
// Create division.
const mainDivision: HTMLDivElement = this.htmlControls.createDivision();
// Create horizontal line.
mainDivision.appendChild(this.htmlControls.createHorizontalLine());
// Create button.
const myButton: HTMLButtonElement = document.createElement("button");
myButton.classList.add('theia-button');
myButton.textContent = "MyButton";
const myButtonEventHandler = (e: Event) => {
console.log("button was clicked!");
};
myButton.addEventListener("click", myButtonEventHandler);
mainDivision.appendChild(myButton);
// Create horizontal line.
mainDivision.appendChild(this.htmlControls.createHorizontalLine());
return mainDivision;
}
Now I created a React class (tsx file) that implements a render(), returning a React.ReactNode object. Though I am having a hard time finding out how to put the results of render() onto the dialog to be visible. I cannot just append it as child to this.contentNode:
import { injectable, postConstruct } from 'inversify';
import { StatefulWidget, Message } from '@theia/core/lib/browser';
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
import * as React from "react";
@injectable()
export class WizardReactTest extends ReactWidget implements StatefulWidget {
constructor() {
super();
}
...
protected render(): React.ReactNode {
return <div>
some dialog text from React
</div>;
}
}
What must I do to get the results of the render() methods as a child into this.contentNode? What is the right way to handle tsx React files? Is there a plain and simple example somewhere?
@jbicker, could you please help to answer this question? Thanks!
Instead of extending the ReactWidget you should extend the ReactRenderer.
Look at the LocationListRenderer as an example and see in FileDialog how it is appended.
So in your simple case:
// no injectable
export class WizardReactTest extends ReactRenderer {
constructor() {
super();
}
...
render(): void {
super.render();
}
protected doRender(): React.ReactNode {
return <div>
some dialog text from React
</div>;
}
}
The render method sets the ReactRenderer host which is by default a simple div element. If you don't want to use such additional div element pass your own to the super constructor.
So in your WizardDialog you can do the following:
...
constructor() {
this.contentNode.appendChild(this.addSomethingReactful());
}
protected addSomethingReactful(): HTMLElement {
const test = new WizardReactTest();
test.render();
return test.host;
}
...
Of course this is very simple and you would call test.render() in something like onUpdateRequest or such.
Don't forget to dispose of WizardReactTest to unmount the react component.
Thanks @jbicker for the example.
@akosyakov Do you mean overriding ReactRenderer.dispose()?
dispose() {
super.dispose();
}
Not overriding but calling WizardReactTest.dispoer when WizardDialog is disposed. You can achieve it by pushing an instance of WizardReactTest to WizardDialog.toDispose collection.