Preparing for a React interview and not sure where to start? This guide walks you through the must-know core concepts — from JSX to Hooks — in a simple, clear way.
We’ll start with the React fundamentals and move step by step into other important topics. For this article we have:
📚 Table of Contents
- JSX and Babel
- Components: Functional vs Class
- Props and PropTypes
- State and Lifecycle
- Conditional Rendering
- Lists and Keys
1. JSX and Babel
What is JSX?
JSX lets you write HTML-like code inside JavaScript. It looks like HTML, but it's really JavaScript.
Why It’s Useful:
- You don’t need to separate your HTML and JS into different files.
- You can easily build UI components that show and update data dynamically.
Example:
const element = <h1>Hello, Reacth1>;
This looks like HTML, but it's JSX — and React understands how to handle it.
What happens to JSX behind the scenes?
JSX doesn't run in the browser directly. React apps use Babel to convert JSX into regular JavaScript.
Behind the scenes, the code above becomes this:
const element = React.createElement('h1', null, 'Hello, React');
What is Babel and why do we need it?
Babel is a tool that translates JSX and modern JavaScript (like ES6) into plain JavaScript (ES5) that browsers can understand.
Why Babel is critical:
- Browsers don’t understand JSX.
- Some older browsers don’t support the latest JavaScript syntax.
- Babel ensures that all your React code (JSX + modern JS) works everywhere.
2. Components: Functional vs Class
In React, components are the core building blocks of your UI. Whether it's a button, a navbar, or an entire page — it's all just components.
There are two main ways to write them:
- Functional Components — the modern, preferred way
- Class Components — the older, legacy style
Functional Components
A functional component is just a JavaScript function that returns JSX. It’s simple, clean, and easy to test.
function Welcome({ name }) {
return <h1>Hello, {name}h1>;
}
Why they’re great:
- Require less boilerplate
- Easier to read and maintain
- You can use React Hooks to manage state, side effects, and context
Hooks Example:
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component mounted or count changed:', count);
}, [count]);
return (
<div>
<p>Count: {count}p>
<button onClick={() => setCount(count + 1)}>Increasebutton>
div>
);
}
Class Components
Before hooks came around (React 16.8), this was the only way to use state and lifecycle methods.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
componentDidUpdate() {
console.log('Component updated');
}
render() {
return (
<div>
<p>Count: {this.state.count}p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Increase
button>
div>
);
}
}
3. Props and PropTypes
Props (short for "properties") are how data flows from a parent component to a child component in React. Think of them like function arguments — they allow you to pass data and callbacks around to keep your components reusable and dynamic.
What Are Props?
Props are immutable that cannot be altered and can be anything: strings, numbers, functions, arrays, or even other components.
Example of Passing Props:
function Greeting({ name }) {
return <h1>Hello, {name}!h1>;
}
function App() {
return <Greeting name="John" />;
}
In the example above:
- The App component passes a name prop to the Greeting component.
- The Greeting component receives the prop and displays the name.
PropTypes: Type Checking in React
PropTypes is a React utility that helps with validating the types of props passed into a component. This is useful to catch errors during development.
Why Use PropTypes?
- Ensures the correct type of data is passed to components.
- Helps document components and provides self-checks.
Example of PropTypes:
import PropTypes from 'prop-types';
function Greeting({ name, age }) {
return <h1>Hello, {name}. You are {age} years old!h1>;
}
Greeting.propTypes = {
name: PropTypes.string.isRequired, // Name must be a string and required
age: PropTypes.number.isRequired, // Age must be a number and required
};
function App() {
return <Greeting name="John" age={30} />;
}
In the example above:
- The Greeting component expects two props: name (a string) and age (a number).
- If you pass an incorrect type or forget to pass a required prop, React will show a warning in the console during development.
Why PropTypes Matter
- Development-Time Safety: PropTypes help catch bugs early by validating the types and presence of props.
- Better Documentation: PropTypes serve as documentation for other developers.
- Debugging: If you pass incorrect or missing props, React will warn you with clear error messages.
Major Takeaways
- Props allow you to pass data between components.
- PropTypes provide a way to check if props are correctly passed with the right data type.
Both make React components more robust, readable, and easier to debug.
4. State and Lifecycle
In React, state is how a component manages and stores data that can change over time. State can be thought of as a local memory for a component. When the state changes, the component re-renders, reflecting the new data.
The lifecycle refers to the series of methods that get called at specific points in a component's existence, like when it's first created, updated, or destroyed. Understanding the lifecycle is key to building efficient React apps.
What is State?
State allows components to hold dynamic data that can change based on user actions or events.
Example of Using State:
import { useState } from 'react';
function DevModeToggle() {
const [isDevMode, setIsDevMode] = useState(false);
return (
<div>
<button onClick={() => setIsDevMode(!isDevMode)}>
{isDevMode ? 'Disable Dev Mode' : 'Enable Dev Mode'}
button>
{isDevMode && <p>Developer mode is ONp>}
div>
);
}
In this example:
- useState is a hook that creates a state variable.
- isDevMode holds the current state (true or false), and setIsDevMode is the function used to update it.
- Clicking the button toggles the state between enabled and disabled, showing conditional content based on that state.
Why is State Important?
- Dynamic data: It allows components to react to changes, making the UI interactive.
- Encapsulation: State is local to the component, meaning other components can’t directly modify it (unless you pass data down as props)
The Lifecycle Methods (Class Components)
In class components, React provides lifecycle methods that let you hook into key moments of a component’s life. Here are the main ones:
- componentDidMount(): Called once when the component is first rendered.
- componentDidUpdate(): Called when the component is re-rendered due to state or props change.
- componentWillUnmount(): Called right before the component is removed from the DOM.
Example of Lifecycle Methods in Class Components:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
componentDidMount() {
console.log('Component mounted');
}
componentDidUpdate() {
console.log('Component updated');
}
componentWillUnmount() {
console.log('Component will unmount');
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<p>Count: {this.state.count}p>
<button onClick={this.increment}>Increasebutton>
div>
);
}
}
In this example:
- componentDidMount() runs once when the component is mounted.
- componentDidUpdate() runs when the state changes.
- componentWillUnmount() runs just before the component is removed.
The Lifecycle with Hooks (Functional Components)
With React 16.8 and hooks, functional components gained the ability to manage state and lifecycle events without using classes.
The most common hooks are:
- useState for state management.
- useEffect for lifecycle-like behavior.
Example with useEffect:
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component mounted or updated');
}, [count]); // Dependency array - runs when count changes
return (
<div>
<p>Count: {count}p>
<button onClick={() => setCount(count + 1)}>Increasebutton>
div>
);
}
In this example:
- useEffect acts like both componentDidMount and componentDidUpdate. It runs when the component mounts and every time count changes.
Why useEffect Is Great:
- Combines multiple lifecycle methods into one.
- Handles side effects like fetching data, subscribing to events, and manual DOM manipulations.
Key Takeaways
- State is how components store dynamic data that can change over time.
- Lifecycle methods allow you to run code at specific times in a component’s life (e.g., mounting, updating, unmounting).
- With hooks (useState, useEffect), functional components can handle state and lifecycle events, simplifying code.
5. Conditional Rendering
In React, you’ll often need to render certain parts of the UI conditionally based on the state, props, or user interactions. Conditional rendering allows you to create dynamic UIs where elements show up only when necessary.
React provides several ways to handle conditional rendering, including if-else statements, ternary operators, logical AND (&&), and switch-case statements.
Conditional Rendering with if Statements
You can use the standard JavaScript if statement to render different components or elements based on a condition.
Example:
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome back!h1>;
} else {
return <h1>Please log in.h1>;
}
}
function App() {
const isLoggedIn = true;
return <Greeting isLoggedIn={isLoggedIn} />;
}
In this example:
- The Greeting component checks the isLoggedIn prop and renders different messages based on its value.
Conditional Rendering with Ternary Operator
For simpler conditionals, the ternary operator (? :) is often a more compact solution. This operator is like a shorthand if-else statement.
Example:
function Greeting({ isLoggedIn }) {
return (
<h1>{isLoggedIn ? 'Welcome back!' : 'Please log in.'}h1>
);
}
function App() {
const isLoggedIn = true;
return <Greeting isLoggedIn={isLoggedIn} />;
}
Here:
- The Greeting component uses the ternary operator to decide which message to display.
Conditional Rendering with Logical AND (&&)
Another common way to render a component conditionally is using the logical AND operator (&&). This works well when you want to render something only when a condition is true.
Example:
function Profile({ user }) {
return (
<div>
{user.isLoggedIn && <h1>Welcome, {user.name}!h1>}
div>
);
}
function App() {
const user = { isLoggedIn: true, name: 'John' };
return <Profile user={user} />;
}
In this example:
- The Profile component only renders the if user.isLoggedIn is true.
Conditional Rendering with Switch Statements
For more complex conditions or when you have multiple cases to check, you might prefer using a switch statement.
Example:
function Greeting({ status }) {
switch (status) {
case 'loggedIn':
return <h1>Welcome back!h1>;
case 'guest':
return <h1>Guest, please sign up!h1>;
case 'banned':
return <h1>Sorry, you've been banned.h1>;
default:
return <h1>Unknown statush1>;
}
}
function App() {
const status = 'loggedIn';
return <Greeting status={status} />;
}
Here:
- The Greeting component uses a switch statement to render different messages based on the status prop.
Summary of Conditional Rendering Patterns
Method | Description | Example Usage |
---|---|---|
if statement | Use when rendering multiple elements based on a condition. | Showing a login message based on authentication. |
Ternary operator | Shorthand for if-else, best for simple conditions. | Conditionally showing text or components. |
Logical && | Renders something only when the condition is true. | Showing a greeting if the user is logged in. |
switch statement | Use for multiple conditions. | Handling different user statuses. |
Key Takeaways
- Conditional rendering lets you display components based on conditions.
- Use if, ternary operators, &&, or switch statements to control what gets rendered.
- It’s essential for building dynamic UIs where content changes based on user interaction or data.
6. Lists and Keys
When working with React, lists are a common feature. Whether you're displaying multiple items from an array or dynamically rendering a set of components, React provides an efficient way to work with lists. However, when rendering lists, React needs to know how to track each element for updates, which is where keys come into play.
Why Lists Are Important in React
React allows you to render lists of data by mapping over an array and returning components. Each element in the list can then be uniquely identified and updated when necessary.
Example of Rendering a List:
const items = ['Apple', 'Banana', 'Cherry'];
function FruitList() {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}li>
))}
ul>
);
}
In this example:
- The map() function iterates over the items array and renders each item inside an
- element.
- A key (key={index}) is assigned to each
- to help React track and efficiently re-render the list items when the data changes.
The Role of key in Lists
In React, keys help React identify which items have changed, been added, or been removed. This is crucial for performance, as it helps React minimize re-rendering operations. Keys should be unique and stable for each item in the list.
Why Keys Are Important:
- Performance: Helps React quickly identify what needs to change.
- Predictability: Without keys, React might get confused, especially if the list changes order, and end up re-rendering unnecessary elements.
Best Practices for Keys:
- Use a unique and stable value (like an ID from a database or a unique string) instead of using the index, if possible.
- Avoid using indexes as keys if the list can change dynamically (e.g., adding, removing, or reordering items), because it can cause React to mismanage the component state.
Example of Lists with Stable Keys:
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Doe' }
];
function UserList() {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}li>
))}
ul>
);
}
In this example:
- The id from the users array is used as a stable and unique key for each
- .
- This ensures React handles updates correctly, even if the list changes.
Performance Optimization with Lists and Keys
React optimizes the process of updating the DOM by using keys to identify each element uniquely. When you modify a list (e.g., adding, removing, or rearranging elements), React will only update the specific items that changed rather than re-rendering the entire list.
Example of List with Dynamic Updates:
const [items, setItems] = useState(['Apple', 'Banana', 'Cherry']);
function addItem() {
setItems([...items, 'Orange']);
}
function removeItem(index) {
setItems(items.filter((_, i) => i !== index));
}
function ItemList() {
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>
{item} <button onClick={() => removeItem(index)}>Deletebutton>
li>
))}
ul>
<button onClick={addItem}>Add Itembutton>
div>
);
}
In this example:
- The list of items can be dynamically updated by adding or removing items.
- React efficiently updates the DOM only for the elements that change, improving performance.
Key Takeaways
- Lists in React can be rendered by mapping over an array of data and returning components.
- Keys help React track list items for efficient re-rendering.
- Always use unique and stable keys for list items to prevent unnecessary re-renders and bugs.
Let’s Connect
If you found this helpful, check out my other posts on React hooks like useEffect, useState, and more.
I’m sharing beginner-friendly content to help you build a solid foundation — step by step.
You can also find me here: