Learning Objectives
- Differentiate between function scope and block scope.
- Explain hoisting and the temporal dead zone.
- Identify when to use
let,const, orvar. - Apply best practices in React components.
Understanding let and const vs var in JavaScript
Welcome! In this lesson, we'll explore the differences between var, let, and const — three ways to declare variables in JavaScript. Understanding these distinctions is crucial for writing clean, predictable, and bug-free React code.
Before ES6 (ECMAScript 2015), JavaScript only had var for variable declarations. ES6 introduced let and const to address common pitfalls. Today, modern JavaScript (and React) heavily favors let and const.
Note: const does not mean the value is immutable. It means the variable binding cannot be reassigned. For objects and arrays, their contents can still change.
Scope defines where a variable is accessible.
varis function-scoped: it is accessible anywhere inside the function it is declared in, regardless of block boundaries (likeiforfor).letandconstare block-scoped: they are only accessible within the nearest enclosing block{ ... }.
// var – function scoped
function varExample() {
if (true) {
var x = 10;
}
console.log(x); // 10 (accessible outside the if block)
}
varExample();
// let / const – block scoped
function letExample() {
if (true) {
let y = 20;
const z = 30;
}
console.log(y); // ReferenceError: y is not defined
console.log(z); // ReferenceError: z is not defined
}
letExample();
Why block scope matters
Block scope prevents accidental variable leakage and makes code more predictable. In React, this is especially helpful inside event handlers, hooks, and conditional renders.
All variable declarations are hoisted (moved to the top of their scope during compilation), but their initialization behaves differently.
var: The declaration is hoisted and initialized withundefined. You can access the variable before its declaration (but getundefined).letandconst: The declaration is hoisted but not initialized. Accessing them before the declaration results in aReferenceErrorbecause they are in a Temporal Dead Zone (TDZ).
// var hoisting
console.log(a); // undefined (no error)
var a = 5;
// let TDZ
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;
// const TDZ
console.log(c); // ReferenceError: Cannot access 'c' before initialization
const c = 15;
Warning: Relying on var hoisting can lead to subtle bugs. Always declare variables at the top of their scope.
var: Can be redeclared multiple times in the same scope without error.let: Cannot be redeclared in the same scope.const: Cannot be redeclared and cannot be reassigned after initialization.
// var redeclaration
var name = "Alice";
var name = "Bob"; // Allowed
console.log(name); // Bob
// let redeclaration
let age = 25;
let age = 30; // SyntaxError: Identifier 'age' has already been declared
// const redeclaration & reassignment
const PI = 3.14;
PI = 3.14159; // TypeError: Assignment to constant variable
const PI = 3.14; // SyntaxError: redeclaration
Use const by default. Only use let when you know the variable must be reassigned (e.g., loop counters, accumulators). Avoid var entirely in modern code.
In React components, you'll constantly work with variables. Here's how to choose:
constfor:- Component declarations (function components).
- Props destructuring.
- State setters from
useState. - Immutable values that never change.
letfor:- Mutable values inside a component's logic (e.g., accumulators, temporary variables).
- Avoid
var– it has no advantage overlet/constand introduces scope issues.
// React component example
const MyComponent = ({ initialCount }) => {
const [count, setCount] = useState(initialCount); // const for state & setter
let message = "No clicks yet"; // let because it may change
if (count > 0) {
message = `Clicked ${count} times`;
}
const handleClick = () => { // const for function
setCount(count + 1);
};
return (
<div>
<p>{message}</p>
<button onClick={handleClick}>Click me</button>
</div>
);
};
export default MyComponent; // const for export
var– function-scoped, hoisted withundefined, can be redeclared. Avoid.let– block-scoped, hoisted but not initialized (TDZ), cannot be redeclared in the same scope. Use when reassignment is needed.const– block-scoped, hoisted but not initialized, cannot be reassigned or redeclared. Use as the default.
Remember: In React, prefer const for almost everything, and use let only when you must mutate the binding.
Key Takeaway
"Use
constby default. Useletwhen you need to reassign. Never usevar."