React does not do any static analysis to determine if a function is a component.
Instead, React will do "it's virtual DOM thing"
(and all the other magic like lifecycles)
if the function returns a valid React element.
React throws an error if the function doesn't return a React element.
https://reactjs.org/docs/components-and-props.html:
This function is a valid React component because it accepts a single “props” (which stands for properties) object argument with data and returns a React element. We call such components “function components” because they are literally JavaScript functions.
In more detail ...
Technically React doesn't need any functional or class components at all.
Using functions
Consider this example, without any components or functions:
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
React.createElement( 'div', null, [
React.createElement( 'h1', null, 'React without any functions' ),
React.createElement( 'p', null, 'Technically you don\'t need to pass functions to React.' ),
]),
document.getElementById('root')
);
This quickly becomes cumbersome.
So one might have the idea to separate the code into "parts" by using
functions which return these parts instead, e.g.:
const Headline = function(){
return React.createElement( 'h1', null, 'React with "factory functions"' );
};
const Content = function(){
return React.createElement( 'p', null, 'You might want to separate stuff using functions.' );
};
const Page = function(){
return React.createElement( 'div', null, [
Headline(),
Content()
]);
};
ReactDOM.render(
Page(),
document.getElementById('root')
);
Note that these are just normal javascript functions, no "magic" at all.
If your function doesn't return a valid ReactElement, then React just throws an error, e.g.:
const Page = function(){
return { notAValidReactElement: 'this is not a valid react element' };
};
ReactDOM.render(
Page(), // ---> Uncaught Error: Objects are not valid as a React child ...
document.getElementById('root')
);
React "component" type
But this is not optimal, e.g. all .createElement()
methods are still always executed at the same time.
The React people had similar ideas, so React accepts a function name
as type
parameter (instead of e.g. 'h1'
), and uses that function like a callback, so that React can decide e.g. if the function
actually needs to be executed, or can be skipped:
const Page = function(){
return React.createElement( 'div', null, [
React.createElement( Headline, null, [] ),
React.createElement( Content, null, [] )
]);
}
These are still the same functions as before. They just work because we wrote them so that they return a correct value.
This is actually more or less the full story regarding this topic, but people might also be confused why I am
talking about React.createElement( Page, ...
, while we are normally using something like <Page>...
.
So ...
JSX
One might have the idea to shorten the React.createElement
a bit, for less typing, e.g.:
const CE = function( type, props, childs ){
return React.createElement( type, props, childs );
};
ReactDOM.render(
CE( 'div', null, [
CE( 'h1', null, 'React without any functions' ),
CE( 'p', null, [
CE('b', null, 'Nice'),
' if we can shorten the code a bit.',
] ),
]),
document.getElementById('root')
);
Luckily smart people have had this idea already, so you can use JSX instead, e.g.:
ReactDOM.render(
<div>
<h1>React without any functions</h1>
<p>
<b>Even nicer: </b>
we can just use JSX!
</p>
</div>,
document.getElementById('root')
);
That's just a different syntax, that gets converted into javascript at build time.
(Maybe also have a look at Hyperscript for comparison and background understanding.)