React Fundamentals and Concepts Guide
React Fundamentals and Concepts Guide
Table of Contents
1. Fundamentals
2. Components and JSX
3. State and Props
4. Hooks
5. Advanced Concepts
6. Performance Optimization
7. State Management
8. Routing and Navigation
9. Testing and Best Practices
SECTION 1: FUNDAMENTALS
Q1: What is React and why is it used?
Answer:
React is a JavaScript library developed by Facebook for building user interfaces with
reusable components. It uses a declarative approach to build UIs efficiently and maintain
application state.
Key Benefits:
Component-Based Architecture: Build encapsulated components that manage their
own state
Virtual DOM: Improves performance by minimizing direct DOM manipulation
Unidirectional Data Flow: Makes application logic predictable and easier to debug
SEO Friendly: Better search engine optimization with server-side rendering support
Large Community: Extensive ecosystem and third-party libraries
Code Example:
);
}
export default App;
Hello, World!
;
// Compiled JavaScript (what browser understands)
const element = [Link]('h1', null, 'Hello, World!');
// JSX with attributes and expressions
const name = 'John';
const element = (
Hello, {name}
Current year: {new Date().getFullYear()}
);
Q3: What is the Virtual DOM and how does it work?
Answer:
The Virtual DOM is an in-memory representation of the real DOM. React uses it to improve
performance by minimizing expensive DOM operations.
How It Works:
1. When a component's state or props change, React creates a new Virtual DOM tree
2. React compares the new Virtual DOM with the previous one (reconciliation)
3. React calculates the minimum number of changes needed (diffing algorithm)
4. React updates only the changed elements in the real DOM (batch updates)
Benefits:
Improves performance by reducing direct DOM manipulations
Provides abstraction layer between application code and browser DOM
Enables efficient rendering of large lists and complex UIs
Code Example:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
);
}
export default Counter;
Q4: What are the differences between React and other frameworks?
Answer:
React differs from frameworks like Angular and Vue in several ways:
Feature React Angular Vue
Type Library Framework Framework
Learning Moderat
Steep Gentle
Curve e
Bundle Size Smaller Larger Smallest
No (Change
Virtual DOM Yes Yes
Detection)
Data Binding One-way Two-way Two-way
Very
Community Large Growing
Large
JavaScrip JavaScript/TypeSc
Language TypeScript
t ript
React supports two types of components: functional components and class components.
Functional Components:
JavaScript functions that return JSX
Simpler and more concise
Recommended approach in modern React
Can use hooks for state and side effects
Class Components:
ES6 classes that extend [Link]
More verbose but provide full lifecycle control
Use [Link] and [Link] for state management
Older approach, still supported but less common
Code Example:
// Functional Component
function Welcome(props) {
return
Hello, {[Link]}!
;
}
);
}
// Class Component
class Welcome extends [Link] {
render() {
return
Hello, {[Link]}!
;
}
}
// Class Component with state
class Counter extends [Link] {
constructor(props) {
super(props);
[Link] = { count: 0 };
}
render() {
return (
Count: {[Link]}
);
}
}
Q6: What are fragments and why are they useful?
Answer:
Fragments are a way to group multiple elements without adding extra DOM nodes. They
are useful when a component needs to return multiple elements.
When to Use Fragments:
);
}
// With Fragment - short syntax
function ListItemsShort() {
return (
<>
Item 1
Item 2
Item 3
</>
);
}
// With Fragment - long syntax (allows key prop)
function ListItemsWithKey() {
const items = ['Item 1', 'Item 2', 'Item 3'];
return (
<>
{[Link]((item, index) => (
<[Link] key={index}>
{item}
</[Link]>
))}
</>
);
}
export default ListItemsShort;
Props (Properties):
Passed from parent to child component
Read-only; child cannot modify props
Used to pass data and functions down the component tree
Component receiving props cannot change them
State:
return (
);
}
// Child Component receiving props
function Child(props) {
const [childState, setChildState] = useState('Child Data');
return (
<div>
Props from Parent:
Name: {[Link]}
Age: {[Link]}
Message: {[Link]}
<h2>Own State:</h2>
<p>State: {childState}</p>
<button onClick={() => setChildState('Updated Child Data')}>
Update State
</button>
</div>
);
}
</[Link]>
);
}
function Level1() {
return ;
}
function Level2() {
return ;
}
function Level3() {
const user = useContext(UserContext);
return
User: {[Link]}
;
}
Controlled Components:
Form elements are controlled by React state
Value is always in sync with state
React is the "single source of truth"
Recommended approach
Uncontrolled Components:
{[Link]}
{[Link]} Submit
);
}
// Uncontrolled Component
function UncontrolledForm() {
const nameRef = useRef();
const emailRef = useRef();
Submit
);
}
SECTION 4: REACT HOOKS
Q10: What is the useState hook and how does it work?
Answer:
useState is a hook that lets functional components manage state. It returns an array with
two elements: the current state value and a function to update it.
Syntax:
Code Example:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
return (
<div>
Count: {count}
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
<h2>Name: {name}</h2>
<input
value={name}
onChange={(e) => setName([Link])}
placeholder="Enter name"
/>
</div>
);
}
// Lazy initialization
function ExpensiveComponent() {
// computeInitialState() runs only once on mount
const [state, setState] = useState(() => computeInitialState());
return
{state}
;
}
function computeInitialState() {
[Link]('Computing initial state...');
return 42;
}
export default Counter;
Syntax:
useEffect(() => {
// side effect logic here
return () => {
// cleanup logic here (optional)
};
}, [dependencies]);
Dependency Array Behavior:
function FetchData() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Runs once on mount (like componentDidMount)
let isMounted = true;
fetchUserData();
;
return
{[Link](data)}
;
}
// Multiple effects for different concerns
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
useEffect(() => {
// Effect 1: Fetch user data when userId changes
fetch(/api/users/${userId})
.then(res => [Link]())
.then(data => setUser(data));
}, [userId]);
useEffect(() => {
// Effect 2: Fetch posts when userId changes
fetch(/api/users/${userId}/posts)
.then(res => [Link]())
.then(data => setPosts(data));
}, [userId]);
return (
{user &&
{[Link]}
}
{[Link] > 0 &&
Posts: {[Link]}
);
}
export default FetchData;
My App
Switch to {theme === 'light' ? 'dark' : 'light'} mode
</header>
);
}
// App setup
function App() {
return (
);
}
export default App;
Q13: What is the useRef hook and when should you use it?
Answer:
useRef creates a mutable reference object that persists across re-renders. Unlike state,
updating a ref doesn't trigger a re-render.
Use Cases:
Accessing DOM elements directly (focus, text selection)
Storing mutable values that don't affect rendering
Keeping track of timers or intervals
Integrating with third-party libraries
Code Example:
<button onClick={triggerRender}>
Trigger Re-render (count: {rerenders})
</button>
</div>
);
}
// Storing interval ID
function Timer() {
const intervalRef = useRef(null);
const [seconds, setSeconds] = useState(0);
const startTimer = () => {
[Link] = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval([Link]);
};
return (
Seconds: {seconds}
Start Stop
);
}
export default TextInput;
Q14: What are custom hooks and how do you create them?
Answer:
Custom hooks are reusable functions that use React hooks internally. They allow you to
extract component logic into reusable functions. A custom hook is a JavaScript function
whose name starts with "use" and may call other hooks.
fetchData();
return () => {
isMounted = false;
};
}, [url]);
return { data, loading, error };
}
;
if (error) return
Error loading user
;
return (
{user?.name}
{[Link]}
);
}
// Custom Hook: useLocalStorage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = [Link](key);
return item ? [Link](item) : initialValue;
} catch (error) {
[Link](error);
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
[Link](key, [Link](valueToStore));
} catch (error) {
[Link](error);
}
};
return [storedValue, setValue];
}
Q15: What is the useReducer hook and when should you use it?
Answer:
useReducer is an alternative to useState for managing complex state logic. It's useful when
you have multiple state variables that depend on each other or complex state transitions.
Syntax:
const [state, dispatch] = useReducer(reducer, initialState);
When to Use:
Complex state logic with multiple transitions
Multiple related state values
State updates that depend on previous state
Large component state with many interactions
Code Example:
return (
Count: {[Link]}
);
}
// Complex example with multiple fields
const initialState = {
name: '',
email: '',
message: '',
submitted: false
};
{[Link]}
{[Link]}
Submit
<button type="button" onClick={() => dispatch({ type: 'RESET' })}>
Reset
</button>
{[Link] &&
Form submitted successfully!
);
}
export default Counter;
const value = {
isAuthenticated,
user,
login,
logout
};
return (
<[Link] value={value}>
{children}
</[Link]>
);
}
// Step 3: Create custom hook for consuming context
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
{isAuthenticated ? (
Welcome, {[Link]}!
Logout
):(
<button onClick={() => login({ name: 'John Doe' })}>
Login
</button>
)}
);
}
// App setup
function App() {
return (
);
}
export default App;
Q17: What are Higher-Order Components (HOCs) and how do you create
them?
Answer:
A Higher-Order Component is an advanced pattern for reusing component logic. It's a
function that takes a component and returns a new enhanced component with additional
functionality.
When to Use:
Code reuse and logic abstraction
State abstraction and manipulation
Props manipulation
Adding additional markup
Permission/authentication checks
Code Example:
[Link](() => {
// Check if user is authenticated
const checkAuth = async () => {
try {
const response = await fetch('/api/auth/check');
if ([Link]) {
setIsAuthenticated(true);
}
} catch (error) {
[Link]('Auth check failed:', error);
}
};
checkAuth();
}, []);
if (!isAuthenticated) {
return <div>Please log in to access this page</div>;
}
};
}
return (
<div style={{
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#000' : '#fff',
padding: '20px'
}}>
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'dark' : 'light'} mode
</button>
<WrappedComponent theme={theme} {...props} />
</div>
);
};
}
// Original component
function Dashboard(props) {
return (
Dashboard
Theme: {[Link]}
);
}
// Enhanced components
const AuthenticatedDashboard = withAuth(Dashboard);
const ThemedDashboard = withTheme(Dashboard);
const AuthenticatedThemedDashboard = withAuth(withTheme(Dashboard));
export default AuthenticatedThemedDashboard;
Q18: What are React Portals and when are they used?
Answer:
Portals provide a way to render components outside of their parent DOM hierarchy. This is
useful for modals, tooltips, dropdowns, and overlays that need to visually break out of their
parent container.
Use Cases:
Modal dialogs
Dropdowns and tooltips
Loading spinners
Notifications and alerts
Context-aware overlays
Code Example:
return (
<div>
My App
<button onClick={() => setIsModalOpen(true)}>
Open Modal
</button>
<Modal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
>
<h2>Modal Title</h2>
<p>This is a modal rendered using Portal</p>
</Modal>
</div>
);
}
export default App;
Q19: What is code splitting and [Link]?
Answer:
Code splitting is a technique to split code into smaller bundles and load them on-demand.
[Link] enables dynamic imports with Suspense for lazy loading components.
Benefits:
return (
<div>
<button onClick={() => setPage('dashboard')}>Dashboard</button>
<button onClick={() => setPage('settings')}>Settings</button>
<button onClick={() => setPage('profile')}>Profile</button>
// pages/[Link]
function Dashboard() {
return
Dashboard Page
;
}
export default Dashboard;
Q20: What are Error Boundaries and how do you implement them?
Answer:
Error Boundaries are React components that catch JavaScript errors anywhere in the
component tree and display a fallback UI. They help prevent the entire app from crashing.
Limitations:
Only work with class components
Don't catch errors in event handlers (use try-catch)
Don't catch async errors
Don't catch server-side errors
Don't catch errors in the error boundary itself
Code Example:
componentDidCatch(error, errorInfo) {
[Link]('Error caught:', error);
[Link]('Error info:', errorInfo);
// You can also log to an error reporting service
}
render() {
if ([Link]) {
return (
<div style={{
padding: '20px',
background: '#ffe0e0',
color: '#c00',
borderRadius: '8px'
}}>
return [Link];
}
}
);
}
// Usage
function App() {
return (
My App
);
}
Code Example:
import React from 'react';
// Without [Link] - re-renders every time parent renders
function ProductCard(props) {
[Link]('ProductCard rendering:', [Link]);
return (
<div style={{ border: '1px solid #ccc', padding: '10px' }}>
{[Link]}
Price: ${[Link]}
</div>
);
}
// With [Link] - only re-renders if props change
const MemoizedProductCard = [Link](ProductCard);
// Custom comparison function
const CustomMemoProductCard = [Link](
ProductCard,
(prevProps, nextProps) => {
return [Link] === [Link] && [Link] === [Link];
}
);
function App() {
const [count, setCount] = [Link](0);
return (
<div>
Count: {count}
);
}
export default App;
Code Example:
import React, { useMemo, useCallback, useState } from 'react';
function ExpensiveCalculation() {
const [count, setCount] = useState(0);
const [items, setItems] = useState([1, 2, 3, 4, 5]);
// Without useMemo - recalculates every render
// const total = [Link]((sum, item) => sum + item, 0);
// With useMemo - calculates only when items change
const total = useMemo(() => {
[Link]('Calculating total...');
return [Link]((sum, item) => sum + item, 0);
}, [items]);
Total: {total}
);
}
// Child component wrapped with [Link]
const ItemList = [Link](function ItemList({ items, onAddItem }) {
[Link]('ItemList rendering');
return (
Items: {[Link]}
Add from child
);
});
export default ExpensiveCalculation;
Q23: What are performance optimization best practices?
Answer:
Key performance optimization techniques:
1. Code Splitting and Lazy Loading
2. Memoization ([Link], useMemo, useCallback)
3. Virtual list rendering (react-window)
4. Avoiding inline functions and objects
5. Proper use of keys in lists
6. Avoiding unnecessary state
7. Using production builds
8. Profiling with React DevTools
Code Example:
import React, { useState, useCallback, useMemo } from 'react';
import { FixedSizeList as List } from 'react-window';
// Bad: Inline function and object
function BadOptimization() {
const [items, setItems] = useState([]);
return (
<div>
{/* Creates new function every render */}
<button onClick={() => [Link]('clicked')}>
Click me
</button>
);
}
);
}
{items[index]}
);
return (
{Row}
);
}
{[Link](item => (
// Use stable unique identifier, not index
{[Link]}
))}
);
}
// Action types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const RESET = 'RESET';
// Action creators
const increment = () => ({ type: INCREMENT });
const decrement = () => ({ type: DECREMENT });
const reset = () => ({ type: RESET });
// Reducer
const initialState = { count: 0 };
);
}
function App() {
return (
);
}
export default App;
if (!email || !password) {
setError('Both fields are required');
return;
}
if () {
setError('Invalid email format');
return;
}
};
return (
<input
type="email"
value={email}
onChange={(e) => setEmail([Link])}
placeholder="Enter email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword([Link])}
placeholder="Enter password"
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
Login
);
}
export default LoginForm;
// Test file
import { render, screen, fireEvent } from '@testing-library/react';
import LoginForm from './LoginForm';
describe('LoginForm Component', () => {
test('renders login form', () => {
render();
expect([Link]('Enter email')).toBeInTheDocument();
expect([Link]('Enter password')).toBeInTheDocument();
expect([Link]('button', { name: /login/i })).toBeInTheDocument();
});
test('shows error when fields are empty', () => {
render();
const submitButton = [Link]('button', { name: /login/i });
[Link](submitButton);
expect([Link]('Both fields are required')).toBeInTheDocument();
});
});
expect(consoleSpy).toHaveBeenCalledWith('Form submitted:', {
email: 'test@[Link]',
password: 'password123'
});
[Link]();
});
});
1. Component Structure:
Keep components small and focused
Single Responsibility Principle
Clear naming conventions
2. State Management:
Lift state only when necessary
Keep state as local as possible
Use Context API or Redux for shared state
3. Performance:
Use [Link] for pure components
Implement proper key prop in lists
Lazy load components and routes
Avoid unnecessary re-renders
4. Code Quality:
Write meaningful comments
Use PropTypes or TypeScript
Follow consistent code style
Regular code reviews
5. Accessibility:
Use semantic HTML
ARIA labels where needed
Keyboard navigation support
Test with accessibility tools
Code Example:
import React, { useState, useCallback, memo } from 'react';
import PropTypes from 'prop-types';
// Parent component
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, title: 'Learn React', completed: false },
{ id: 2, title: 'Build project', completed: false }
]);
const handleToggle = useCallback((id) => {
setTodos(prevTodos =>
[Link](todo =>
[Link] === id ? { ...todo, completed: ![Link] } : todo
)
);
}, []);
My Todo List
{[Link](todo => (
))}
);
}
export default TodoList;
componentDidCatch(error, errorInfo) {
// Log to error reporting service
logErrorToService(error, errorInfo);
}
render() {
if ([Link]) {
return
fetchData();
return () => {
isMounted = false;
};
}, []);
if (error) return
Error: {[Link]}
;
if (!data) return
Loading...
;
return
{[Link](data)}
;
}
// Helper function
function logErrorToService(error, errorInfo) {
[Link]('Error logged:', error);
// Send to error tracking service (Sentry, LogRocket, etc.)
}
export { ErrorBoundary, FormSubmit, DataFetch };
CONCLUSION
This comprehensive guide covers:
Key Takeaways
1. Master hooks: useState, useEffect, useContext, useRef, useReducer
2. Understand component lifecycle and rendering
3. Know when to use Context API vs Redux
4. Implement proper error handling
5. Optimize performance proactively
6. Write testable and accessible components
7. Follow best practices consistently
8. Keep components focused and reusable
9. Think in terms of user behavior, not implementation
10. Stay updated with React ecosystem changes