function versus const for React components
In React, we can write components using the function
keyword or the const
keyword and an arrow function.
Using const and an arrow function
const MyComponent = () => {
return(
<p>My component</p>
)
}
Using the function keyword
function MyComponent() {
return (
<p>My component</p>
);
}
The first approach creates a function using the JavaScript arrow function syntax and stores it in a constant variable. The second approach with the function
keyword creates a plain function that most of us are used to.
Both are functions that will perform the exact same task, which is to return the React component's JSX code for rendering.
Using const
and arrow functions has become more popular lately. Many developers find the function
keyword syntax more verbose.
Is there a difference between the two approaches or can we always use either approach interchangeably? There is one important difference between the two. That difference is related to hoisting.
Hoisting
Hoisting is something that JavaScript does during its compile phase, just microseconds before code is executed. That code is scanned for function and variable declarations. All functions and variable declarations are added to memory inside a JavaScript data structure. This is so that they can be used even before they are actually declared in the source code.
helloWorld();
// prints 'Hello World' to the console
function helloWorld() {
console.log('Hello World');
}
Since function declarations are added to memory during the compile stage, we are able to access the helloWorld()
function even before it is declared.
Hoisting with let
Using a let
variable before it is declared will result in a ReferenceError
. The variable username
is in a "Temporal Dead Zone" from the start of the code block until it is actually declared using let username = "Mario"
.
try {
username = "Luigi";
let username = "Mario";
}
catch(err) {
console.log(err);
// ReferenceError: Cannot access 'username' before initialization
}
Hoisting with const
Using an uninitialized const
variable before it is declared is a syntax error. The code will throw an error.
try {
username = "Luigi";
const username;
}
catch(err) {
console.log(err);
// Uncaught SyntaxError: Missing initializer in const declaration
}
If we do initialize the const
declaration, like we did with let
, we will get the same error as we got with let
above.
try {
username = "Luigi";
const username = "Mario";
}
catch(err) {
console.log(err);
// ReferenceError: Cannot access 'username' before initialization
}
The function
, let
, and const
declarations are all hoisted. However, the let
and const
declarations remain uninitialized. They are only initialized when the variable assignment is evaluated at runtime by the JavaScript engine. This means that we cannot access let
and const
variables before the JavaScript engine evaluates their values at the place where they were declared. This is what is referred to as a "Temporal Dead Zone." It is a time span between variable creation and variable initialization where variables cannot be accessed.
Hoisting and React components
Let's apply what we learned from the hosting of const
variables to React components.
const App = () => (
<>
<MyComponent />
<MyOtherComponent />
</>
);
export default App;
const MyComponent = () => {}
const MyOtherComponent = () => {}
In this example, the linter will throw an error because MyComponent
and MyOtherComponent
are used before they are declared.
We can fix the error by moving the component declarations to the top of the file.
const MyComponent = () => {}
const MyOtherComponent = () => {}
const App = () => (
<>
<MyComponent />
<MyOtherComponent />
</>
);
export default App;
If we want to keep our component declarations at the bottom of a file and then use them before they are declared, we should use the function
syntax. This will allow them to be hoisted to the top of the file.
Here is the same example re-written with the function
syntax in order to keep component declarations at the bottom of the file and fix the error.
const App = () => (
<>
<MyComponent />
<MyOtherComponent />
</>
)
function MyComponent() {}
function MyOtherComponent() {}
Conclusion
React components declared with the function
keyword can be called even if their function definition is further down in the code. React components declared with const
and arrow functions will still get hoisted, but they cannot be called until after they are defined.