React Component getting added in Vaadin 7 only on button click - reactjs

I have a vaadin7 application to which I am trying to integrate a sample react application. It is working properly when I implement the React component creation inside a button click. But it is not working if I put the code outside the button click. It shows error "Uncaught TypeError: Cannot read property 'appendChild' of null
at Module.11 (index.js:9)
at u (bootstrap:84)
at t (bootstrap:45)
at Array.r [as push] (bootstrap:32)
at main.90a85973.chunk.js:1" while inspecting.
In vaadin application, i am creating div named "root" and in the index.js of sample react application, I am trying to get the root div created from vaadin and appending "react-root" to "root" div so as to fit the react application in my vaadin layout.
Please find below code for vaadin ui
CustomLayout layout = null;
try {
String dynamicHtml = "<div id=\"root\"></div>";
layout = new CustomLayout(new ByteArrayInputStream(dynamicHtml.getBytes()));
System.out.println("JS Layout");
} catch (IOException e) {
System.out.println("could not create custom layaout"+ e);
}
uimainLayout.addComponent(layout);
//If the below 4 lines of code are added inside the button click, the reactcomponent is rendered inside the layout properly on clicking the button . Otherwise it is showing the "Uncaught TypeError: Cannot read property 'appendChild' of null"
VerticalLayout jsmainLayout = new VerticalLayout();
ReactComponent reactComponent=new ReactComponent();
jsmainLayout.addComponent(reactComponent);
uimainLayout.addComponent(jsmainLayout);
getMainLayout().addComponent(uimainLayout);
setCompositionRoot(getMainLayout());
My Reactcomponent
#JavaScript({"<ip:port>/static/js/2.1f3e22a9.chunk.js","<ip:port>/static/js/3.583f7bad.chunk.js","<ip:port>/static/js/runtime-main.23689a58.js","<ip:port>/static/js/main.90a85973.chunk.js","ReactComponentConnector.js"})
#StyleSheet("<ip:port>/static/css/main.a617e044.chunk.css")
public class ReactComponent extends AbstractJavaScriptComponent {
public ReactComponent() {
System.out.println("Inside ReactComponent");
}
#Override
protected ReactComponentState getState() {
return (ReactComponentState) super.getState();
}
}
ReactComponentState
public class ReactComponentState extends JavaScriptComponentState {
private static final long serialVersionUID = -3283446856435450054L;
}
ReactComponentConnector.js
window.com_p1_p2_ReactComponent = function () {
}
Index.js of my sample react application
const rootEl = document.createElement('div')
rootEl.setAttribute('id', 'react-root');
//error points to the below line
const parentEl =document.getElementById('root');
parentEl.appendChild(rootEl)
ReactDOM.render(
<App />
,
rootEl
);
reportWebVitals();

It's not clear exactly what it is that triggers running the code listed for index.js, but it would seem like it's run before the contents of the custom layout has been initialized. In that way, no element with the id root exists and this would cause the reported error.
I assume creation happens through new ReactComponent(this.getElement()) from inside the JS connector initializer. If this is the case, then they reason for the problem is that both the actions (set custom layout content and initialize the JS connector) are sent to the browser in the same batch, but the connector is initialized in an earlier phase. If the JS component is sent in a batch triggered though a click listener, then this won't be a problem.
To solve this, you could use a direct element reference instead of relying on getElementById in combination with an id defined in the content of a custom layout. The easiest way would be if you'd restructure to use the this.getElement() instance that you're now passing to the ReactComponent constructor.

Related

Replace DOM with JSX in React 18

How to remove this error message from my console
I'm using ReactDOM.render to replace certain "unreachable" parts of my code with JSX components, it worked fine in previous versions but now I'm getting this annoying error message and I want to get rid of it.
Long story:
I'm using the FullCalendar lib for react18 and Nextjs.
I'm facing a limitation from the lib, in previous versions I was able to pass JSX to render in the header buttons, but in the current version 5.11.2 it's not possible anymore, it only let you set either text or a bootstrap/font-awesome icon.
So I instead used an old known trick to replace DOM with no more than the HTML element
ReactDOM.render(
<AnyIconIWantToUse />,
window.document.querySelector("#element-to-replace-id")
)
and that is what brings up the said error message
What I've tried
As the error suggest I've tried using createRoot instead but it gives me an error too (and afaik it's meant to be used only with the root component so I prefer not to use it).
This should help you out
createPortal(
<AnyIconIWantToUse />,
document.getElementById("element-to-replace-id")
)
I ended up achieving what I wanted with another approach.
Instead of replacing DOM content directly with JSX I instead render the desired JSX into the DOM and replace the DOM with DOM
// utils/replaceDOM.ts
import type React from 'react';
import { renderToString } from 'react-dom/server';
type ReplaceDOM = (
elementToReplace: Element,
replacement: React.ReactElement
) => void;
const replaceDOM: ReplaceDOM = (elementToReplace, replacement) => {
if (!replacement) return;
// Get html from component (only get first render)
const replacementHTML = renderToString(replacement);
// Parse html string into html
const parser = new DOMParser();
const parsedDocument = parser.parseFromString(replacementHTML, 'text/html');
const replacementElement = parsedDocument.body.children[0];
// Append replacement to DOM
window.document.body.prepend(replacementElement);
// Replace children with element
elementToReplace.replaceWith(replacementElement);
};
export default replaceDOM;
Then I can use it as desired
replaceDOM(elementToReplace, <ElementIWant className="w-6" />);

how to know the component rendering condition in react js

I have one component which is rendering as child as well or user can navigate to that component directly from menu of UI. I am facing one issue now is, if that component as child then its working fine but when I click upon menu to navigate directly then my getDerivedStateFromProps() function is throwing an error as
TypeError: Cannot read property 'length' of undefined
Below is my code. It will work fine if I send a data from parent component to this component and render it from parent component.
static getDerivedStateFromProps(props, state) {
if (props.data.length)// this line is throwing an error when I directly come to this component from menu {
const data = props.data;
return { custData : data };
}
return null;
}
How can I check the condition that not to execute above logic if I am coming to this component directly.
You can double-check like this :
if (props.data && props.data.length)
Check to see if the data exists
if (props?.data)

Control a WebView object in Electron with typescript

I'm trying to generate a instance of a webview using typescript.
I'm using electron-forge's react-typescript template.
I tried the following:
import {Component} from 'react';
import * as React from 'react';
import {WebviewTag} from 'electron';
class MediaWebView extends Component<{ url: string }, {}> {
renderWebVierw() {
const myWebView: WebviewTag /* error here */ =
(<webview src={this.props.url}
autosize='on'
nodeintegration='on'
disablewebsecurity='on'
webpreferences='allowRunningInsecureContent'
style={webViewStyle}
/>);
return myWebView;
}
This renders the webview but throws the following error: TS2322: Type 'Element' is not assignable to type 'WebviewTag'. Property 'addEventListener' is missing in type 'Element'.
I can't suscribe to events or inject code into the webview.
I don't know what type should I be assigning, but I can't seem to find it.
What's the correct way to implement a webview, and have access to its methods using typescript?
const webview: WebViewTag = (<webview />) as WebViewTag;
Had the same problem. The issue is because without the as, the compiler doesn't know that what you're doing is returning a WebViewTag object, and the left-hind side of the equation doesn't care because WebViewTag inherits from HtmlElement. In this case as SomeClass will coerce the object into whatever type you try to cast it to, and if the types match, then it will return the object, if they don't, it will just return null.

catch keydown events in inner components using react-keydown

I'm using react-keydown library for adding keyboard shortcuts to my application, but can't make it work in inner dialogs components. The dialogs are not always visible, but I expect the keys to work when they are visible.
I'm getting to event_handlers.js._onKeyDown(event) method, but with missing value: fn = undefined, whereas fn should contain the decorated function.
My components looks like:
<Container>
<MyDialog>
<material-ui-dialog/>
</MyDialog>
</Container>
Container.js:
import keydown from 'react-keydown'
class Container extends Component {
#keydown('enter')
someMethod1(){
// working
}
render() {
return (
<div>
<MyDialog/>
</div>
)
}
}
MyDialog.js:
import keydown, {keydownScoped} from 'react-keydown'
#keydown('enter')
class MyDialog extends Component {
#keydownScoped('enter')
someMethod3(){
// not working
}
}
Based on your description in the comments, the issue appears to be that your Dialog components mount and then lose focus so any keybindings inside them will not receive the keystrokes. You have a couple of options:
1) Expand the scope of the keybinding by decorating a component that is an ancestor of your Dialog components and won't lose focus. In an extreme case this could be the root component of your app. Then decorate the desired Dialog component method with keydownScoped. Inside that method examine the props to make sure the current dialog is the active one.
2) Programmatically activate your Dialog keybindings along the lines of this https://github.com/glortho/react-keydown/issues/28.
Hope that helps!

Testing a React component that modifies an external element

I have a React component that, when clicked, updates the style of an HTML element that lives outside of it. Here's what that method looks like:
_hideContent() {
let content = document.getElementById("container");
content.style.top = 100vh;
...
}
When I try to test it using TestUtils.simulate.click(), I get an error due to the external HTML element not being found. The error is:
TypeError: Cannot read property 'style' of null
at FilterButtonMobile._hideContent (public/js/FilterButtonMobile/components/FilterButtonMobile.react.jsx:45:14)
...
How can I test this component?

Resources