What is the difference between React Component and React Element? The documentation mentions both but does not go into detail, some methods require components, other element
A React Element is just a plain old JavaScript Object
without own methods. It has essentially four properties:
type
, a String
representing an HTML tag or a reference referring to a React Componentkey
, a String
to uniquely identify an React Elementref
, a reference to access either the underlying DOM node or React Component Instance)props
(properties Object
)A React Element is not an instance of a React Component. It is just a simplified "description" of how the React Component Instance (or depending on the type
an HTML tag) to be created should look like.
A React Element that describes a React Component doesn't know to which DOM node it is eventually rendered - this association is abstracted and will be resolved while rendering.
React Elements may contain child elements and thus are capable of forming element trees, which represent the Virtual DOM tree.
A custom React Component is either created by React.createClass
or by extending React.Component
(ES2015). If a React Component is instantiated it expects a props
Object
and returns an instance, which is referred to as a React Component Instance.
A React Component can contain state and has access to the React Lifecycle methods. It must have at least a render
method, which returns a React Element(-tree) when invoked. Please note that you never construct React Component Instances yourself but let React create it for you.
To further elaborate on the answer, a React Element does not have any methods and nothing on the prototype. This also makes them fast.
"A ReactElement is a light, stateless, immutable, virtual representation of a DOM Element" - Glossary of React Terms
A react component render()
function returns a DOM tree of react elements behind the scenes (This is the virtual DOM btw). There is some complex mapping and diff logic involved, but basically these React elements map to the DOM elements.
You can also create a Element directly React.createElement(arg)
where arg can be a html tag name, or a React Component class.
Here is my take :
Element
is the thing that describes how to construct the VDOM. It's basically a "frozen" version of the corresponding Component Instance
.
If everything would be functional component
then there would be no need for an extra react Element
. The functional component
hierarchy could produce the VDOM tree directly.
A react Component Instance
hierarchy (tree
) is a "factory", and that factory is parametrized by the props which are fed to the root react Component Instance
and by all the state "stored" anywhere in the Component Instance
tree.
So the react Element
is basically an "abstract syntax tree" which gets compiled into the actual VDOM.
So why not generate the VDOM directly by using the react Component Instances
? This is the real question here.
At the first glance I don't see why it would not be possible to do so. So most likely the answer is that it's a question of performance.
The react Element
is one layer of abstraction between the VDOM and the Component Instance
, why this abstraction is needed is not entirely clear to me, most likely it allows optimizations. For example the Element
tree does not get rendered completely if some lifecycle method on the Component Instance
says that there is no need to render that subtree.
Now, in this case, the logic which handles this optimization "needs" this extra layer of indirection - because the information needs to be stored somewhere that some part of the VDOM should not be compared with the new VDOM, even more, maybe the new VDOM should not be even calculated at all. So using an extra layer of indirection makes the rendering logic simpler and leads to a cleaner, more maintainable implementation - I imagine.
This concept in Haskell is called "lifting" :
For example monads in Haskell are perfect examples of such liftings.
A monad is sort of a description of a computation that can be stored as a value, like 42
. Similarly, react Elements
are elments of a description on how to compute the "new" VDOM. If somebody wants to compute it.
This talk describes this concept of an "extra" indirection with some simple examples :
In other words : premature optimization is the root of all evil.
Or :Fundamental theorem of software engineering
The fundamental theorem of software engineering (FTSE) is a term originated by Andrew Koenig to describe a remark by Butler Lampson1 attributed to the late David J. Wheeler:2
We can solve any problem by introducing an extra level of indirection.
So, in my understanding react Elements
are an implementation detail to handle complexity gracefully and allow some optimization (performance). I don't see why one could not get rid of this extra indirection - in principle - react would still work "just as well" - but it might be super slow, implementing and maintaining the react "engine" itself would be probably a nightmare.
Please correct me if I am missing here something.
Quoting an IMPORTANT part of user6445533's answer :
type
, a String representing an HTML tag or a reference referring to a React Component
THIS IS THE KEY ^^^^
Element IS NOT VDOM.
type
properties other than native HTML elements._It's important to make this distinction here because the element is not the actual thing we see on the screen, rather the object representation is what is rendered.
createElement
method.
const element = React.createElement(
'div',
{id: 'login-btn'},
'Login'
)
Here createElement
takes in three arguments
Login
)createElement
invocation returns an object
{
type: 'div',
props: {
children: 'Login',
id: 'login-btn'
}
}
When this is rendered to the DOM (using ReactDOM.render
), we'll have a new DOM node that looks like this:
<div id='login-btn'>Login</div>
Generally React is taught from a components-first approach, however understanding elements-first makes for a smooth transition to components.
A component is a function or a Class which optionally accepts input and returns a React element.
A React Component is a template. A blueprint. A global definition. This can be either a function or a class (with a render function).
If react sees a class or a function as the first argument, it will check to see what element it renders, given the corresponding props and will continue to do this until there are no more createElement
invocations which have a class or a function as their first argument.
When React sees an element with a function or class type, it will consult with that component to know which element it should return, given the corresponding props.
At the end of this processes, React will have a full object representation of the DOM tree. This whole process is called reconciliation in React and is triggered each time setState
or ReactDOM.render
is called.
Class syntax is one of the most common ways to define a React component. While more verbose than the functional syntax, it offers more control in the form of lifecycle hooks.
Create a class component
// MyComponent.js
import React, { Component } from 'react';
class MyComponent extends Component {
render() {
return (
<div>This is my component.</div>
);
}
}
export default MyComponent;
Use it in any other component
// MyOtherComponent.js
import React, { Component } from 'react';
import MyComponent from './MyComponent';
class MyOtherComponent extends Component {
render() {
return (
<div>
<div>This is my other component.</div>
<MyComponent />
</div>
);
}
}
export default MyOtherComponent;
Use props
<MyComponent myProp="This is passed as a prop." />
Props can be accessed with this.props
class MyComponent extends Component {
render() {
const {myProp} = this.props;
return (
<div>{myProp}</div>
);
}
}
Using state
class MyComponent extends Component {
render() {
const {myState} = this.state || {};
const message = `The current state is ${myState}.`;
return (
<div>{message}</div>
);
}
}
Using lifecycle hooks
class MyComponent extends Component {
// Executes after the component is rendered for the first time
componentDidMount() {
this.setState({myState: 'Florida'});
}
render() {
const {myState} = this.state || {};
const message = `The current state is ${myState}.`;
return (
<div>{message}</div>
);
}
}
Function-Based Components
With createElement
function Button ({ addFriend }) {
return React.createElement(
"button",
{ onClick: addFriend },
"Add Friend"
)
}
function User({ name, addFriend }) {
return React.createElement(
"div",
null,
React.createElement(
"p",
null,
name
),
React.createElement(Button, { addFriend })
)
}
With what createElement
returns
function Button ({ addFriend }) {
return {
type: 'button',
props: {
onClick: addFriend,
children: 'Add Friend'
}
}
}
function User ({ name, addFriend }) {
return {
type: 'div',
props: {
children: [
{
type: 'p',
props: {
children: name
}
},
{
type: Button,
props: {
addFriend
}
}
]
}
}
}
Here we have a Button
component which accepts an onLogin
input and returns a React element.
Button
component receives an onLogin
method as its property.id
attribute.There are three related kinds of thing involved here, with their own names:
This is slightly surprising, since if you're used to other UI frameworks you might expect that there'd only be two kinds of thing, roughly corresponding to classes (like Widget
) and instances (like new Widget()
). That's not the case in React; component instances are not the same thing as elements, nor is there a one-to-one relationship between them. To illustrate this, consider this code:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class MyComponent extends React.Component {
constructor(props) {
super(props);
console.log('This is a component instance:', this);
}
render() {
const another_element = <div>Hello, World!</div>;
console.log('This is also an element:', another_element);
return another_element;
}
}
console.log('This is a component:', MyComponent)
const element = <MyComponent/>;
console.log('This is an element:', element);
ReactDOM.render(
element,
document.getElementById('root')
);
In the code above:
MyComponent
(the class itself) is a Componentelement
is an Element. It's not an instance of MyComponent
; rather, it's simply a description of the component instance to be created. It's an object with key
, props
, ref
and type
properties. Here, key
and ref
are null
, props
is an empty object, and type
is MyComponent
.MyComponent
gets created (and, in the example above, logs itself from its constructor) when element
gets rendered.another_element
is also an element, and has key
, ref
, props
and type
properties just like element
does - but this time the value of type
is the string "div"
.The design reasons why React has these three distinct concepts are explored in detail in the React team's blog post React Components, Elements, and Instances, which I recommend reading.
Finally, it should be noted that while the official docs are rigorous about using the term "component" to refer to a function or class and "component instance" to refer to an instance, other sources do not necessarily adhere to this terminology; you should expect to see "component" used (incorrectly) to mean "component instance" when reading Stack Overflow answers or discussions on GitHub.