React render component withtout breaking tree when using other libraries - reactjs

Some situation
I am currently using the js library code-mirror (v5) (a text editor with some nice features, such as syntax highlighting) and i must add widgets to it using its API.
It is quite straightforward, you just give code-mirror's API the html element you created for the occasion and it will add it to the page document inside the dom it generated for the editor.
So i just mount a new react component on the HTML element i fed to the code-mirror API. It is correctly rendered without errors, but...
The Problem
While the method above works I'm quite sure that's not the way to do it for multiple reasons.
The created component lose the parent context (so change to props doesn't update the created component & stores such as redux are inaccessible)
It breaks from the app react tree
It is not cleaned up with the destruction of any component and will just remain in memory until we use ReactDOM.unmountAtNode()
I tried stuff with portals but it doesn't seems to be what i need in this case (if i got it right portals do render things outside of the react tree)
Example
This an example of a method that would add a react component to the codemirror as a widget
function addWidget(component: ReactElement, position: Position): void {
const div = document.createElement('div');
ReactDOM.render(component, div);
//code mirror api
editor.addWidget(position, div, false);
}
Conclusion
I am looking for React best practices in these cases where components needs to be mounted on dynamically created HTML element without losing context and still being attached to some part of the app react tree.
Thank for your time.

Related

How to use existing HTML element in React?

Is it possible to reference existing HTML element in React?
I have a page where React used only for small part of components and I have "video" element that exists on page before React loads. Then I have a react component which have a couple of props that should affect video element.
What is the best/correct way to achieve this?
Currently, in render method of a component, I use document.getElementById('video-' + this.props.videoId) and then manipulating it. I thought that I can somehow use "refs" to say to reuse the existing HTML element, but not sure how and didn't found useful information.
Thanks!
What I understand is, you have an app, probably built in some other stack and you are trying to use React inside that app. The page is loaded before and then the React component renders. As pointed out by Icepickel, refs are for the components that are created by you inside the React app. So, you can't use that here.
Normally, it is discouraged to directly access the elements in the DOM. But since you are using it on a part of it, so it is totally fine. But doing it in the render() method is not the right choice here.
Instead what you can do is, utilize the React lifecycle methods to control the video player in a better way. Normally when a component is mounted on the DOM. Following lifecycle methods are called in the following order:
constructor
componentWillMount
render
componentDidMount
So, what I will suggest is, inside the constructor set the state using document.getElementById('video-' + this.props.videoId). [I am assuming the page laods before the react component].
let el = document.getElementById('video-' + this.props.videoId);
this.state = {
videoPlayer: el;
}
And then later when your component is mounted. Inside the componentDidMount, change whatever you want to change in the video player.
I have also created a small Code Sandbox Sample to elaborate on the lifecycle methods. This way, you will be able to write cleaner code and easily manage the state of the video player.

REACT using Ref

is it true, the purpose of Ref is replacing document.getElementById??
Once i apply react, i should not use document.getElementById to access DOM to get data?
i found some article said that we can apply the this.refs to access the DOM
<input ref="test" value="option" id="option4"/>
console.log(this.refs.test);
But it can only run within the method within the component,
what if i want show the input value in console (developer Tools)?
It is true in a way, because react uses its virtual dom and diffing algorithm to watch changes and reflect in dom. If you use direct api to access dom. react could not access it in its virtual dom.
Thats why they have an unique id or keys just like in DOM to manipulate elements (components) as node.
If you see in confirm-alert components used in npm packages, they will create an element and render it as an element by using ReactDOM. As soon the toaster is finished it is not removed directly from the DOM.
First it is made to be find from the reactVirtualDom by using api findDomNode at https://reactjs.org/docs/react-dom.html#finddomnode
then it is unmounted using unmountComponentAtNode.
https://reactjs.org/docs/react-dom.html#unmountcomponentatnode
For example: https://github.com/GA-MO/react-confirm-alert/blob/master/src/index.js
So, refs are used to overcome direct DOM manipulation and changes that affect or effects the react rendering process.

How to trigger an event after any (re)render of a React/Redux app

A React/Redux app that I develop needs to live inside an iframe. The surrounding document can be told to resize the iframe by sending a message from within. I would like to send the resize message any time the DOM changes.
Using a generic MutationObserver will cause a flickering between the time the DOM is updated and the resizing of the iframe.
The application is set up using
render(
<Provider store={store}>
...
</Provider>,
document.querySelector("..."),
resizeIframe
);
The resizeIframe callback function solves the initial rendering, but does not help with child component updates.
How can I trigger resizeIframe on any React (re)rendering?
Edit: I know resizing on every rendering is inefficient. However, getting rid of the delay between DOM resize and iframe resize appears to be the bigger problem.
You can use componentWillReceiveProps() to do anything you want when props/state changes.
more about react lifecycles
One of the fundamental ideas of react is that calling calling a render method of an element doesn't necessarily change the DOM.
So it doesn't sound like a good idea to respond to every component's render method being called.
You also wrote:
I would like to send the resize message any time the DOM changes
This doesn't sound great, either: changes to the DOM do not necessarily change the size of the document.
It looks like what you need is to detect the size changes to the iframe that your app lives in, or to the root element of your app. A good idea would be to use a library which does exactly this: https://github.com/wnr/element-resize-detector
Start by playing with its convenient listenTo method:
import elementResizeDetectorMaker from 'element-resize-detector';
const erd = elementResizeDetectorMaker();
erd.listenTo(document.getElementById("test"), function(element) {
resizeIframe(); // the function you implemented earlier
});

How are React component instances different than other frameworks?

In the docs, React says that it doesn't really care for instances as the Components take props as input and outputs elements for you. It gives you an example at the top of how other frameworks have to create an instance and then connect it to the DOM to handle different events. But I don't understand how this is different than what React is doing.
You're not calling new on your component in React, but you still have to render it and create all the same handlers. And this inside the component still refers to the instance, so doesn't React still have to create an instance each time your component is rendered (even if it's a component inside an <li> that's being rendered several times at once).
Traditional frameworks will have to create multiple instances of the same component to connect to each DOM node it corresponds to, isn't that what React is doing too? How else can one component keep track of multiple this's?
Indeed, React creates Component instance internally. You don't need to worry about using new.
React Element is just a plain JavaScript Object that describes what you want to be rendered (React.Component or HTML Element, if type is a String).
From the docs:
An element is not an actual instance. Rather, it is a way to tell
React what you want to see on the screen. You can’t call any methods
on the element. It’s just an immutable description object with two
fields: type: (string | ReactClass) and props: Object1.
The difference is that you the developer are not having to write the code to do all that. You just write the render method and your callbacks and let React worry about creating the DOM elements and the component instances and connecting them together.

Components and events after an external library has changed dom structure

Here is scenario:
react renders components
external library changes dom structure and move a part to somewhere else in dom not handled by react
Result:
Every events set are not working anymore and exceptions..
Expected
Events (clicks) work
What do you think about it ?
Is it possible with react to rerender everything based of a new dom structure ?
Dom react structure is really changed, some part have moved somewhere else in DOM
Thanks,
Julien

Resources