Generically adding custom properties to components - reactjs

I am new to reactjs. I am trying to generically add custom properties to react components. Here is my use case ...
For a react component called HelloWorld, I want the DOM to show the attribute data-js-class="HelloWorld". The reason I want to do this is because when I am inspecting the DOM in dev tools, I can quickly identify the component that rendered that piece of the DOM.
I can do the same thing by going into the React tab of dev tools and jump to the generated DOM element.
I can also do the same in the components render() method by adding properties in every single component. But I dont want to do that in every single component. I want it to happen magically at a framework level.
I have tried to backup the React.createElement and write my own custom createElement which eventually calls the original React.createElement() method. It wasnt an elegant solution.
On the base React.Component class, I added a createElement() (to the React.Component.prototype) method. The component's render method calls "this.createElement(...) instead of "React.createElement()". But this is not elegant either because there is JSX too which this solution will not handle.
class HelloWorld extends React.Component {
render() {
return React.createElement(
'h1',
null, //this.props
'Hello world'
)
}
}
... should render
<h1 data-js-class="HelloWorld">Hello World</h1>
What would be the most elegant way of achieving this?

What would be the most elegant way of achieving this?
I believe adding the metadata for the sake of reviewing which component generated the DOM.
You can simply go to the React devtool and find out which DOM node was generated as shown below.

Related

React render component withtout breaking tree when using other libraries

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.

Dynamically rendering React component within HTML

In my React app, there's a module that renders HTML. I created the following little function in the component that renders the HTML.
renderContent(content) {
return { __html: content }
}
And here's how I call it:
<div className="padding-20" dangerouslySetInnerHTML={this.renderContent(this.props.myContent)}></div>
This has worked so far because the section within the component where I needed to render HTML was separated within a particular <div>.
Now, I'd like to render a component, specifically a <form> within this HTML section and the form itself is in a separate component. Trying to figure out how I can do this.
I think I could set an id to a <div> or use specific class that indicates the existence of a component and somehow render the component there. Has anyone tackled this before? If so, I'd appreciate some suggestions.
The answer depends on a few variables. Some Points of clarification with answers depending on your needs.
This form is a React component? Or is it raw HTML?
Ir exists to in ComponentA and you’d like to render it in ComponentB?
React Portals are the solution designed for this exact problem. They allow you to render content from one component to anywhere else in the DOM.
If you use a portal, I don’t believe passing raw HTML will work. Instead, pass as the first argument a div setting your form using dangerouslySetInnerHtml.

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.

Selective children component render

A basic question I need help here.
Whenever this.setState invoked at parent components, all the children components will be rendered. This will cause the performance issue if I have huge amount of child components.
Lets give an example,
Parent Component
handleToggleTick() {
const newObj = Object.assign({}, this.state, { iconName: ''});
this.setState({
iconName: newObj.iconName,
});
}
render() {
return(
<ChildComponentA iconName={this.state.iconName} toggleTick={() => this.handleToggleTick}></ChildComponentA>
<ChildComponentB></ChildComponentA>
<ChildComponentC></ChildComponentA>
)
}
Based on the example above, whenever handleToggleTick invoked from childcomponentA, setState invoked for new iconName. What I want is, only ChildComponentA only the one get render since props.iconName is related to it, but not for childcomponentB and childcomponentC.
I understand there is an option to check shouldComponentUpdate in childcomponent to prevent it get render. But, imagine I have over 100 of childcomponent, would it be frustrating to write over 100 times of shouldComponentUpdate method?
I need help here, please advice!
React doesn't provide any way to render children selectively. The component will either render or not. But I need to highlight a few points why this is not a problem when we use React in practice.
First of all, you don't need to manually implement shouldComponentUpdate for each component. If you don't want to rerender component if its props and state haven't changed, you can just extend from the PureComponent class instead of the Component class. Note that React.PureComponent's shouldComponentUpdate() only uses shallow comparison for state and props. But this shouldn't be a problem if you follow react best practices and avoid mutating the state.
Also, it's not practical to have more than 100 different components in one render method. React always encourages decomposing your UI into smaller components and using component composition. When we follow this approach, components will be nested inside each other in different levels instead of having a large number of components in one render method.
What I'm trying to explain is it's more practical and easy to manage when we compose our component in a nested fashion (2) rather than having lots of components inside a big container component (1).
In your example, if ChildComponentB and ChildComponentC are inside another component called ChildConatainerComponent then we only need to implement shouldComponentUpdate() for ChildConatainerComponent. Then it will automatically stop rendering any child element inside it.
render() {
return(
<ChildComponentA iconName={this.state.iconName}
toggleTick={() => this.handleToggleTick}/>
<ChildConatainerComponent/>
)
}
class ChildConatainerComponent extends PureComponent {
render() {
return (
<div>
<ChildComponentB/>
<ChildComponentC/>
</div>
);
}
}
Another very import concept to keep in mind is calling render function doesn't mean that React recreates all the DOM elements again. The render method only make changes to React virtual DOM which is an in-memory representation of DOM and it's faster than actual DOM. Then React compare versions of virtual DOM which are before the update and after the update and the actual DOM will be updated with only what has actually changed.
Another solution you could consider is moving iconName into ChildComponentA, considering this is the only component related to it.

How to make an easily embeddable React component that displays any other component's state when used as its child?

I'd like to make a React component that, when inserted inside any other component, will display its state in a DIV floated to the right of the screen. Let name it StateExplorer.
Because many StateExplorers can be used on the same page, the DIVs will stack with the same z-index.
The challenge here is to make StateExplorer easily non-verbosely embeddable, like this:
<SomeComponent>
<StateExplorer/>
....
</SomeComponent>
The particular issue here is: how do I hook on componentWillUpdate() so that the StateExplorer always displays up-to-date state? I could use a mixin, but that throws 2 problems:
componentWillUpdate() can be implemented already in the parent component
adding a mixing adds more verbosity; the ideal case is to just add StateExplorer and nothing more.
P.S. I know about React Debug Tools, but it's not so convenient in some cases and adds extra steps before you can see state of a single component.
Sounds like what you need is a Higher-order component.
Here is a good article about it: https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

Resources