Master JavaScript fundamentals for React development. Learn key concepts like variables, functions, classes, and ES6+ features with examples. Before diving deep into React components, hooks, or state management, you must have a solid understanding of JavaScript fundamentals. React is built on JavaScript — not a separate language, but a library that uses modern JavaScript features extensively.
Table of Contents
Whether you’re a PHP developer shifting to React, a WordPress developer learning modern front-end, or a beginner starting your React journey, this guide will help you understand the core JavaScript concepts used in React development with definitions, examples, and summaries.
Variables and Scope
Variables in JavaScript store data values and are one of the JavaScript Fundamentals for React. React uses variables to manage dynamic UI states, props, and configuration values.
JavaScript provides three ways to declare variables: var, let, and const. Each has distinct characteristics regarding scope, hoisting behavior, and reassignment rules.
var is the original keyword for declaring variables in JavaScript. It is function-scoped or globally scoped, depending on where it’s declared. Variables declared with var can be updated and re-declared within the same scope.
let declares variables with block scope, allowing updates but not re-declaration within the same block. This makes it more predictable and less prone to bugs compared to var.
const declares block-scoped variables that cannot be reassigned after their initial assignment. However, for objects and arrays, the properties or elements can still be modified.
Types
- var — function-scoped
- let — block-scoped
- const — block-scoped and constant
Example
Arrow Functions
Functions are reusable blocks of code and are one of the JavaScript fundamentals for React. In React, they are used to create functional components, handle events, and manipulate data.
Arrow functions provide a concise syntax for writing functions in JavaScript, and are one of the JavaScript Fundamentals for React. They were introduced in ES6 and offer several advantages, particularly in React development
Key Features
Arrow functions do not create their own this context. Instead, they inherit the this value from the enclosing scope, which can be helpful in callback functions and event handlers.
For single expressions, arrow functions can use implicit return without the return keyword and curly braces:
Example
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function with explicit return
const add = (a, b) => {
return a + b;
};
// Arrow function with implicit return
const add = (a, b) => a + b;
// Single parameter - parentheses optional
const square = num => num * num;
Example: Arrow Functions in React
import React from 'react';
// React component using arrow function
const MyComponent = () => {
const handleClick = () => {
console.log('Button clicked');
};
return (
<button onClick={handleClick}>
Click me
</button>
);
};
export default MyComponent;
Summary
Arrow functions make code concise and preserve the this context — important when handling events in React (e.g., onClick handlers).
Arrow functions simplify React component definitions and event handlers. They eliminate the need for manual function binding in class components because they automatically inherit the parent scope’s this context. This makes your code more concise and eliminates common bugs related to this binding.
Objects Destructuring
Objects group related data and functions. Destructuring is a JavaScript expression and one of JavaScript Fundamentals for React that allows you to extract values from objects or arrays into distinct variables. This feature is extensively used in React for handling props and state.
Example
// Without destructuring
const user = {
name: 'John',
age: 30,
city: 'New York'
};
const name = user.name;
const age = user.age;
const city = user.city;
// With destructuring
const { name, age, city } = user;
console.log(name); // John
console.log(age); // 30
// Renaming variables
const { name: userName, age: userAge } = user;
console.log(userName); // John
// Default values
const { country = 'USA' } = user;
console.log(country); // USA
Array Destructuring
const numbers = [1, 2, 3, 4, 5]; // Destructuring arrays const [first, second, ...rest] = numbers; console.log(first); // 1 console.log(second); // 2 console.log(rest); // [3, 4, 5] // Skipping elements const [, , third] = numbers; console.log(third); // 3
Example: Destructuring in React
import React, { useState } from 'react';
// Props destructuring in function parameters
function Greeting({ name, age }) {
return <h1>Hello, {name}! You are {age} years old.</h1>;
}
// Destructuring in component body
function UserCard(props) {
const { name, email, avatar } = props;
return (
<div>
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
// Destructuring useState
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
Summary
Destructuring makes your React code cleaner and more readable. It’s particularly useful for extracting props, state values, and hook returns. React developers use destructuring extensively to avoid repetitive props.propertyName syntax and to work with the values returned by hooks like useState.
Template Literals
Template literals (also called template strings) are string literals that allow embedded expressions, multi-line strings, and string interpolation. They use backticks (`) instead of quotes.
Example
// Traditional string concatenation
const name = 'John';
const age = 30;
const greeting = 'Hello, my name is ' + name + ' and I am ' + age + ' years old.';
// Template literal
const greeting = `Hello, my name is ${name} and I am ${age} years old.`;
// Multi-line strings
const message = `
This is a multi-line
string that spans
multiple lines.
`;
// Expression evaluation
const a = 10;
const b = 20;
const result = `The sum of ${a} and ${b} is ${a + b}.`;
console.log(result); // The sum of 10 and 20 is 30.
Example: Template Literals in React
import React from 'react';
function UserProfile({ user }) {
return (
<div>
<h1>{`Welcome, ${user.firstName} ${user.lastName}!`}</h1>
<p>{`You have ${user.notifications} new notifications.`}</p>
</div>
);
}
export default UserProfile;
Summary
Template literals make string construction cleaner and more readable. In React, they’re particularly useful for creating dynamic class names, constructing URLs, and formatting text. They eliminate the need for cumbersome string concatenation and make your code more maintainable.
Classes and this Keyword
Classes are templates for creating objects and one of the JavaScript Fundamentals for React. React class components extend from React.Component.
Example
class Person {
constructor(name) {
this.name = name;
}greet() {
console.log(`Hi, I'm ${this.name}`);
}
}const user = new Person("Umang");
user.greet();
Summary
Although React now prefers functional components, understanding classes is essential for maintaining legacy React apps.
Spread and Rest Operators
The Spread Operator
The spread operator (...) helps you expand iterables into individual elements. It’s effective within array literals, function calls, and property objects.
Example: Spread Operator
// Spreading arrays
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
// Spreading objects
const person = { name: 'John', age: 30 };
const newPerson = { ...person, city: 'New York' };
console.log(newPerson); // { name: 'John', age: 30, city: 'New York' }
// Combining objects
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const combined = { ...obj1, ...obj2 };
console.log(combined); // { a: 1, b: 2, c: 3, d: 4 }
The Rest Operator
The rest operator (...) collects the remaining elements of an array or object into a new array or object.
// Rest in arrays
const numbers = [1, 2, 3, 4, 5];
const [first, ...rest] = numbers;
console.log(rest); // [2, 3, 4, 5]
// Rest in objects
const user = { name: 'John', age: 30, city: 'New York', country: 'USA' };
const { name, ...otherDetails } = user;
console.log(otherDetails); // { age: 30, city: 'New York', country: 'USA' }
// Rest parameters in functions
function sum(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
Example: Spread/Rest in React
import React, { useState } from 'react';
// Passing props with spread operator
function MyButton() {
const props = {
type: 'button',
className: 'btn-primary',
disabled: false
};
return <button {...props}>Click Me</button>;
}
// Rest operator with props
function Modal({ onClick, ...rest }) {
return (
<div {...rest}>
<button onClick={onClick}>Close</button>
</div>
);
}
// Updating state immutably with spread
function UserProfile() {
const [user, setUser] = useState({
name: 'Alice',
preferences: { theme: 'light', notifications: true }
});
function updatePreferences(newPreferences) {
setUser({
...user,
preferences: { ...user.preferences, ...newPreferences }
});
}
return (
<button onClick={() => updatePreferences({ theme: 'dark' })}>
Toggle Theme
</button>
);
}
Summary
The spread operator is essential for React development because it allows you to create copies of arrays and objects without mutating the originals. This is crucial for maintaining immutability in React state updates. The rest operator helps you collect remaining props and pass them down to child components efficiently.
Array Methods: map(), filter(), and reduce()
The map() Method
The map() method creates a new array by applying a function to each element of the original array. It’s one of the most frequently used array methods in React.
Syntax:
arr.map((element, index, array) => {
// return transformed element
});
Example: map() in JavaScript
const numbers = [1, 2, 3, 4, 5];
// Multiply each number by 2
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// Transform objects
const users = [
{ firstName: 'John', lastName: 'Doe' },
{ firstName: 'Jane', lastName: 'Smith' }
];
const fullNames = users.map(user => `${user.firstName} ${user.lastName}`);
console.log(fullNames); // ['John Doe', 'Jane Smith']
Example: map() in React
import React from 'react';
function UserList() {
const users = [
{ id: 1, name: 'Robin' },
{ id: 2, name: 'Markus' }
];
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
The filter() Method
The filter() method creates a new array containing only elements that pass a test implemented by a provided function.
Example: filter() in JavaScript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8];
// Get numbers greater than 5
const filtered = numbers.filter(num => num > 5);
console.log(filtered); // [6, 7, 8]
// Filter objects
const users = [
{ name: 'John', age: 25 },
{ name: 'Jane', age: 30 },
{ name: 'Bob', age: 20 }
];
const adults = users.filter(user => user.age >= 25);
console.log(adults); // [{ name: 'John', age: 25 }, { name: 'Jane', age: 30 }]
Example: filter() in React
import React, { useState } from 'react';
function SearchableList() {
const [query, setQuery] = useState('');
const users = [
{ id: 1, name: 'Robin' },
{ id: 2, name: 'Markus' },
{ id: 3, name: 'Robert' }
];
return (
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search users..."
/>
<ul>
{users
.filter(user => user.name.toLowerCase().includes(query.toLowerCase()))
.map(user => (
<li key={user.id}>{user.name}</li>
))
}
</ul>
</div>
);
}
The reduce() Method
The reduce() method applies a function to an accumulator and each array element to reduce it to a single value.
Syntax:
arr.reduce((accumulator, currentValue, index, array) => {
// return updated accumulator
}, initialValue);
Example: reduce() in JavaScript
const numbers = [1, 2, 3, 4, 5];
// Sum all numbers
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 15
// Count occurrences
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const count = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(count); // { apple: 3, banana: 2, orange: 1 }
Example: Combining map(), filter(), and reduce()
const data = [1, 2, 3, 4, 5, 6, 7, 8]; const result = data .map(item => item * 2) // [2, 4, 6, 8, 10, 12, 14, 16] .filter(item => item > 10) // [12, 14, 16] .reduce((acc, item) => acc + item, 0); // 42 console.log(result); // 42
Example: reduce() in React
import React from 'react';
function SuperHeroStats() {
const heroes = [
{ id: 101, name: 'Ironman', strength: 1000 },
{ id: 102, name: 'Thor', strength: 10000 },
{ id: 103, name: 'Hulk', strength: 15000 }
];
const totalStrength = heroes.reduce(
(acc, hero) => acc + hero.strength,
0
);
return (
<div>
<h2>Combined Strength: {totalStrength}</h2>
<ul>
{heroes
.filter(hero => hero.strength > 800)
.map(hero => (
<li key={hero.id}>
{hero.name}: {hero.strength}
</li>
))
}
</ul>
</div>
);
}
Summary
These three array methods are fundamental to React development. The map() method is essential for rendering lists of components, filter() helps implement search and filtering functionality, and reduce() is useful for computing derived values from arrays. Understanding these methods eliminates the need for traditional loops and makes your code more functional and declarative.
Modules: Import and Export
ES6 Module System
JavaScript’s ES6 module system allows you to organize code into separate files and share functionality between them. React applications heavily rely on this module system for component organization.
Default Export and Import
A file can have one default export.
// MyComponent.js
import React from 'react';
const MyComponent = () => {
return <h1>Hello from MyComponent!</h1>;
};
export default MyComponent;
// App.js
import MyComponent from './MyComponent';
function App() {
return (
<div>
<MyComponent />
</div>
);
}
export default App;
Named Export and Import
Multiple named exports can exist in a single file.
// utils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;// App.js
import { add, subtract, multiply } from './utils';console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
console.log(multiply(5, 3)); // 15
Combining Default and Named Exports
// components.js
import React from 'react';export const Header = () => <h1>Welcome to My Website</h1>;
export const Footer = () => <footer>© 2023 My Website</footer>;export default function MainContent() {
return <div>This is the main content.</div>;
}
// App.js
import MainContent, { Header, Footer } from './components';
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
Import All
// utils.js export const add = (a, b) => a + b; export const subtract = (a, b) => a - b;// App.js import * as MathUtils from './utils';console.log(MathUtils.add(5, 3)); // 8 console.log(MathUtils.subtract(5, 3)); // 2
Summary
The import/export syntax is JavaScript Fundamentals for React. It allows you to create modular, reusable components and utilities. Use default exports for primary components and named exports for utility functions or when exporting multiple items from a single file.
Promises and Async/Await
Understanding Promises
Promises provide a more convenient API to handle asynchronous operations. They represent the eventual success or failure of an asynchronous operation.
A Promise can be in one of three states:
-
Pending: Initial state, neither fulfilled nor rejected
-
Fulfilled: Operation completed successfully
-
Rejected: Operation failed
Example: Basic Promise
// Creating a Promise
const fetchData = new Promise((resolve, reject) => {
setTimeout(() => {
const data = { user: 'John', age: 30 };
resolve(data); // Success
// reject('Error occurred'); // Failure
}, 1000);
});// Using a Promise
fetchData
.then(data => {
console.log('Data received:', data);
})
.catch(error => {
console.error('Error:', error);
});
Async/Await
The async/await syntax provides a cleaner way to work with promises. The async keyword declares an asynchronous function that returns a promise. The await keyword makes JavaScript wait until a promise settles and returns its result.
Example: Async/Await Syntax
// Traditional promise chaining
function fetchUserData() {
fetch('https://api.example.com/user')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
}// Using async/await
async function fetchUserData() {
try {
const response = await fetch('https://api.example.com/user');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
Example: Async/Await in React
import React, { useState, useEffect } from 'react';
function UserComponent() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
try {
const response = await fetch('https://api.example.com/user');
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return null;
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
export default UserComponent;
Promise Methods
Understanding these Promise methods is helpful for React development:
// Promise.all - Wait for all promises
const fetchMultipleResources = async () => {
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/posts').then(res => res.json()),
fetch('/api/comments').then(res => res.json())
]);return { users, posts, comments };
};// Promise.race - Return first settled promise
const fetchWithTimeout = async () => {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 5000)
);const data = await Promise.race([
fetch('/api/data').then(res => res.json()),
timeout
]);
return data;
};
Summary
Promises and async/await are one of the JavaScript Fundamentals for React and essential for handling asynchronous operations in React, such as API calls, file operations, and timers. The async/await syntax makes asynchronous code more readable and easier to maintain. In React, you’ll frequently use these patterns within useEffect hooks to fetch data when components mount or when dependencies change.
Closures
A closure is a function that retains access to its outer function’s variables, even after the outer function has finished executing. Closures are JavaScript fundamentals for React and appear frequently in React code.
Example: Basic Closure
function start() {
const firstName = 'John';// displayFirstName creates a closure
function displayFirstName() {
console.log(firstName); // Accesses outer variable
}displayFirstName(); // Prints "John"
}start();
// Counter with closure
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Example: Closures in React
import React, { useCallback, useEffect } from 'react';
// Every function in React component is a closure
const Component = () => {
const onClick = () => {
// This is a closure
console.log('Button clicked');
};
return <button onClick={onClick}>Click Me</button>;
};
// Closures in useEffect
function TimerComponent() {
const [count, setCount] = React.useState(0);
useEffect(() => {
// This closure captures the current count value
const timer = setInterval(() => {
console.log(`Count is: ${count}`);
}, 1000);
return () => clearInterval(timer);
}, [count]); // Dependencies matter for closures
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
// Stale closures - common React issue
function StaleClosureExample() {
const [value, setValue] = React.useState('');
const handleSubmit = useCallback(() => {
// This closure captures the initial value
// It won't update unless dependencies change
console.log(value);
}, []); // Empty deps - closure is stale
const handleSubmitCorrect = useCallback(() => {
// This closure updates when value changes
console.log(value);
}, [value]); // Correct dependencies
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}
Summary
Closures are everywhere in React components, and one of the JavaScript Fundamentals for React. Every function defined inside a component is a closure that has access to the component’s props, state, and other variables. Understanding closures helps you avoid common React bugs, particularly “stale closures” where callbacks capture outdated values. This is why dependency arrays in hooks like useEffect and useCallback are so important.
DOM vs Virtual DOM
-
DOM (Document Object Model) represents the web page.
-
Virtual DOM is React’s lightweight copy that efficiently updates changes.
Example Summary
In vanilla JS:
In React:
React updates the Virtual DOM first, then synchronizes changes to the real DOM — improving performance.
Event Handling and Callbacks
Definition
Events like clicks, inputs, or key presses are handled using functions and one of the JavaScript Fundamentals for React. React uses camelCase event names and passes functions as event handlers.
Example
Summary
Understanding how to handle events using callback functions is key to building interactive React apps.
References
Conclusion
Learning React without strong JavaScript fundamentals for React is like building a house on sand. Mastering variables, functions, objects, ES6 syntax, async programming, and modular code organization forms the bedrock for becoming a proficient React developer.
Once you’re comfortable with these concepts:
-
Start writing simple components.
-
Work with useState and useEffect hooks.
-
Build small projects (like Todo or Weather apps).
Every React feature — from JSX to Hooks — is powered by JavaScript. Strengthen these fundamentals, and React will feel natural to you.