how does the .render() function in react work internally? - javascript

I can define a component like this:
class Welcome extends React.Component {
render() {
return <h1>Hello!</h1>;
}
}
When I want to render this object in the Dom (or rather add it to the virtual DOM) I call
ReactDOM.render(
<Welcome />,
document.getElementById('root')
);
That means that at some point multiple .render() functions are nested inside of each other since we defined return <h1>Hello!</h1>; inside of the .render() function of Welcome.
It also means that I can use the .render() method to render an Object to the DOM and to define a new react object.
This syntax is from the official documentation
Does .render() just render things to the virtual DOM and the nesting is resolved by React internally or is there more behind it?

From Rendering a Component:
(updated to reflect your code)
class Welcome extends React.Component {
render() {
return <h1>Hello!</h1>;
}
}
ReactDOM.render(
<Welcome />,
document.getElementById('root')
);
We call ReactDOM.render() with the <Welcome /> element.
React calls the Welcome component with {} as the props.
Our Welcome component returns a <h1>Hello!</h1> element as the result.
React DOM efficiently updates the DOM to match <h1>Hello</h1>.
It may be beneficial to read up on the React Life Cycle.

To understand render you have to understand how react works internally. To understand this in greater detail I would recoment reading this but here is a quick overview:
Conceptually a react component is a just a function that returns an Object like this:
{
type: 'button',
props: {...}
};
So the render() part of a class component just specifies what part of the component will be returned. The react virtual dom is made up of many of these object nested inside of each other (in props.children). React will start at the top object, turn it into an html node and render it to the dom, then do the same for all of its children etc.

Related

reactjs : Diffrence between regular/arrow functions and react functional components

What difference you see between regular/arrow functions and react functional component
React functional component
import React from 'react';
function MyComp(){
return <span>test</span>
}
Regular function
function myFun(){
return null;
}
Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called props) and return React elements describing what should appear on the screen.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
From a JavaScript perspective, this is nothing but a function, except for a special return. It's JSX and each JSX element is just syntactic sugar for calling React.createElement(component, props, ...children).
Basically, you can replace the JSX in the example above with something like:
React.createElement('h1', null, `Hello ${props.name}`);
And this is nothing but a JavaScript :)
And then you can render your component:
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
You call ReactDOM.render() with the <Welcome name="Sara" /> element.
React calls the Welcome component with {name: 'Sara'} as the props.
Our Welcome component returns a <h1>Hello, Sara</h1> element as the result.
React DOM efficiently updates the DOM to match <h1>Hello, Sara</h1> and you get it on the screen.
There is one important requirement for React functional component!
Always start component names with a capital letter.
React treats components starting with lowercase letters as DOM tags. For example, represents an HTML div tag, but represents a component and requires Welcome to be in scope.
This function from your example can be used as a functional component:
function MyFun() { // name should start with a capital
return null;
}
React won't display anything because React does not render if component returns null.

React. Cannot get the component element in the DOM

I'm trying to insert the innerHTML for the div container ed. But I cannot get it after the React has render it. I understand that it's the problem with the stages of render, because I get null for this div container. What is I'm making the wrong?
class Test extends React.Component {
render() {
return (
<div>
<div id='ed'>
<p>{this.props.prop.text}</p>
</div>
{document.querySelector('#ed').innerHTML = this.props.prop[1]} // this the problem
</div>
);
}
}
ReactDOM.render(
<Test prop={store.getState()} />,
document.getElementById('root')
);
Your direct DOM manipulation won't work cause you called it in render().
You called Query selector in render(), Query selector or findDOMNode() only works on mounted components (that is, components that have been placed in the DOM).
If you try to call this on a component that has not been mounted yet (like calling Query selector or findDOMNode() in render() on a component that has yet to be created) an exception will be thrown.
you can do expressions in render() usually, but you can't get access the DOM element in render() since it is placing your elements in render() to DOM.
Use lifeCycle methods instead and You can use ReactDOM.findDOMNode(this) to access the underlying DOM node. But accessing the DOM node and manipulating like you do is against the React style of programming.
Query selector shouldn't be necessary with react just attach a ref to the element you want and you have access to it within any function of the react component.
Example Demo : demo
Try using the lifecycle event componentDidMount
class Test extends React.Component {
componentDidMount() {
const element = document.querySelector('#ed');
if (element) {
element.innerHTML = this.props.prop[1]
}
}
render() {
return (
<div>
<div id='ed'>
<p>{this.props.prop.text}</p>
</div>
</div>
);
}
}
You need to wait for the component to mount. You can do this by putting your code in a componentDidMount method.
componentDidMount() {
document.querySelector('#ed').innerHTML = "woo"
}
You may also reference the container div with ref={node => this.node = node}

Child component constructor called multiple times

I have a parent component which is a flat list which contains a header HeaderComponent. This HeaderComponent is a custom component that I have created which contains 2 child components of its own. Whenever i refresh the list, I am passing a boolean to the HeaderComponent as props which get passed onto its own children, I am doing this so I can check if each component needs to fetch new data or not. The problem is that whenever the parent refreshes and sets a new state the constructors of the child components get called everytime. Shouldn't the constructor be called only the first time the parent initializes and then all further calls involve calling the shouldComponentUpdate method of the children in order to see if it needs an update or not.
Parent component
_renderHeader = () => {
return <HeaderComponent Items={this.state.Data} refresh={this.state.refresh}/>;
};
render() {
console.log("TAG_RENDER render called " + this.state.refresh);
return (
<FlatList
refreshing={this.state.refresh}
onRefresh={() => {
console.log("onRefresh");
this.setState({
refresh: true
}, () => {
this._fetchData();
});
}}
......
ListHeaderComponent={() => this._renderHeader()}
.......
/>
);
}
Header Component
export default class HeaderComponent extends React.Component {
constructor(props) {
super(props);
console.debug("HeaderComponent");
}
render() {
return (
<MainHeader Items={this.props.Items}/>
<SubHeader refresh={this.props.refresh}/>
);
}
}
The constructor of MainHeader and Subheader gets called whenever the parent component refreshes. Does this mean that it is creating new child components each time it refreshes because I can see the render of the children also being called multiple times.
Control your index.js file. If you see <React.StrictMode>, you should change to <>. This is solved my problem.
It should be like:
ReactDOM.render(
<>
<App/>
</>,
document.getElementById('root')
);
As correctly stated in the one of the answers , removing the strict mode fixes the issue. Coming to why it does that, its because the strict mode intentionally calls the 'render' method twice in order to detect potential problems.
React works in two phases:render and commit. Render phase checks and determines the new changes to be applied. And commit phase applies it.
Render phase lifecycle includes methods like : constructor, UNSAFE_componentWillMount,UNSAFE_componentWillReceiveProps, ...,render and few more.
The render phase is time consuming and is often broken into pieces to free up the browser. Render phase might be called multiple times before the commit phase(usually very fast).
Since the render phase methods are called more than once, its important that none of those method have any problems or side effects.
Thus just in order to highlight the possible side effects to make them easy to spot, react explicitly double invoke the render phase methods.
You can read more about this on :https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects :)
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate
methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer
https://reactjs.org/docs/strict-mode.html
As stated in the site,
Note:
This only applies to development mode. Lifecycles will not be double-invoked in production mode.

Is it possible to pass context into a component instantiated with ReactDOM.render?

TL;DR Given the following example code:
ReactDOM.render(<MyComponent prop1={someVar} />, someDomNode);
Is it possible to manually pass React context into the instance of MyComponent?
I know this sounds like a weird question given React's nature, but the use case is that I'm mixing React with Semantic UI (SUI) and this specific case is lazy-loading the contents of a SUI tooltip (the contents of the tooltip is a React component using the same code pattern as above) when the tooltip first displays. So it's not a React component being implicitly created by another React component, which seems to break context chain.
I'm wondering if I can manually keep the context chain going rather than having components that need to look for certain data in context AND props.
React version: 0.14.8
No. Before react 0.14 there was method React.withContext, but it was removed.
However you can do it by creating HoC component with context, it would be something like:
import React from 'react';
function createContextProvider(context){
class ContextProvider extends React.Component {
getChildContext() {
return context;
}
render() {
return this.props.children;
}
}
ContextProvider.childContextTypes = {};
Object.keys(context).forEach(key => {
ContextProvider.childContextTypes[key] = React.PropTypes.any.isRequired;
});
return ContextProvider;
}
And use it as following:
const ContextProvider = createContextProvider(context);
ReactDOM.render(
<ContextProvider>
<MyComponent prop1={someVar} />
</ContextProvider>,
someDomNode
);
In React 15 and earlier you can use ReactDOM.unstable_renderSubtreeIntoContainer instead of ReactDOM.render. The first argument is the component who's context you want to propagate (generally this)
In React 16 and later there's the "Portal" API: https://reactjs.org/docs/portals.html

Grouping a large set of ReactDOM.render() calls in a root component

Let's say I have a large HTML document with numerous React Components embedded in parts of it.
For example:
<body>
<h1 id="react-component-1">...</h1>
<p>Static Content</p>
<h2> id="react-component-2">...</h2>
<img src="static-img" />
<p id="react-component-3">...</h3>
</body>
As you can see, there are static elements in-between my dynamic elements. I'd imagine that this situation is common in web sites that are slowly bringing React in to there toolchain.
Right now my application works like this:
function renderPage(data) {
ReactDOM.render(<Component {...data} />, document.getElementById("react-component-1"));
ReactDOM.render(<Component {...data} />, document.getElementById("react-component-2"));
ReactDOM.render(<Component {...data} />, document.getElementById("react-component-3"));
}
renderPage() is called upon the success of an ajax request. This makes my components update as intended, and it seems pretty performant.
However, it would be much nicer if I had a root React component that handled the state of the application, and pushed down changes to data as props.
I can't make a root component though because not everything on my page is dynamic, I need to preserve the static content in-between the React components.
I would love to be able to do something like this though:
class Root extends React.Component {
constructor(props) {
super(props);
this.data = {...};
}
render() {
ReactDOM.render(<Component {...this.state.data} />, document.getElementById("react-component-1"));
ReactDOM.render(<Component {...this.state.data} />, document.getElementById("react-component-2"));
ReactDOM.render(<Component {...this.state.data} />, document.getElementById("react-component-3"));
}
return false;
}
Is it possible to use React this way? At first I unsure of how it would be possible, because ReactDOM expects an element to render components in to, which would clear out my static content when I render my root component. Now I am thinking I could just do new Root(), but I have yet to test this.
Another option that I just thought of was keeping the renderPage() function and calling it ONCE instead of every time AJAX is loaded. Each component would then listen for a custom event that is fired on the AJAX success, which contains the new data. This seems slower though... And I might as well use Redux for the application state.
You shouldn't use the render method of a React component to call ReactDOM.render the way you do it in your example Root component. The render method of a component can return either a valid React element (for example, <div />) or null and is not supposed to do any mutations, such as modifying the DOM, etc. If a React component needs to do some DOM mutations, it should do it from lifecycle hooks such as ComponentDidMount or ComponentDidUpdate.
To answer you question, it depends whether your static content, can be included in a React component or not.
If you can, which would be ideal, you can refactor your code to have a Root component like so :
class Root extends React.Component {
render() {
return (
<Component data={ this.props.data } />
<p>Static Content</p>
<Component data={ this.props.data } />
<img src="static-img" />
<Component data={ this.props.data } />
);
}
}
and to have one call to ReactDOM.render :
ReactDOM.render(<Root data={ data } />, document.getElementById('my-react-mounting-point'));
If you cannot, that is if you need to have different React render trees, your current approach is fine. If you do this, though, be careful to call ReactDOM.unmountComponentAtNode when you do not need the component anymore, or you might face memory leak issues. This article from React's blog has several nice tips about the precautions to use when having various render trees + a ReactComponentRenderer boilerplate to avoid common pitfalls.

Categories