[go: up one dir, main page]

0% found this document useful (0 votes)
8 views11 pages

React Intermediate

The document outlines advanced React interview questions and answers for candidates with 3 years of experience, covering topics such as advanced hooks, Context API, performance optimization, error boundaries, and testing strategies. It includes code examples for implementing concepts like useReducer, custom hooks, theme context, memoization, render props, and modal components. Additionally, it provides common interview tips to prepare candidates for technical interviews in React.

Uploaded by

mukesh.khanijo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views11 pages

React Intermediate

The document outlines advanced React interview questions and answers for candidates with 3 years of experience, covering topics such as advanced hooks, Context API, performance optimization, error boundaries, and testing strategies. It includes code examples for implementing concepts like useReducer, custom hooks, theme context, memoization, render props, and modal components. Additionally, it provides common interview tips to prepare candidates for technical interviews in React.

Uploaded by

mukesh.khanijo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 11

REACT INTERVIEW QUESTIONS - 3 YEARS EXPERIENCE

1. ADVANCED HOOKS

Q: Explain useReducer and when to use it over useState


A:
useReducer is useful for complex state logic that involves multiple sub-values or
when the next state depends on the previous one.

Example:
```jsx
import React, { useReducer } from 'react';

const todoReducer = (state, action) => {


switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, {
id: Date.now(),
text: action.payload,
completed: false
}]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
default:
return state;
}
};

function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, { todos: [] });

const addTodo = (text) => {


dispatch({ type: 'ADD_TODO', payload: text });
};

const toggleTodo = (id) => {


dispatch({ type: 'TOGGLE_TODO', payload: id });
};

return (
<div>
<button onClick={() => addTodo('New todo')}>Add Todo</button>
{state.todos.map(todo => (
<div key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.text} - {todo.completed ? 'Done' : 'Pending'}
</div>
))}
</div>
);
}
```

Q: Create custom hooks for common functionality


A:
```jsx
// Custom hook for API calls
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

fetchData();
}, [url]);

return { data, loading, error };


}

// Custom hook for localStorage


function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});

const setValue = (value) => {


try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};

return [storedValue, setValue];


}
```

2. CONTEXT API AND STATE MANAGEMENT


Q: Implement a theme context with React Context API
A:
```jsx
import React, { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

function ThemeProvider({ children }) {


const [theme, setTheme] = useState('light');

const toggleTheme = () => {


setTheme(prev => prev === 'light' ? 'dark' : 'light');
};

const themeStyles = {
light: { backgroundColor: '#ffffff', color: '#333333' },
dark: { backgroundColor: '#333333', color: '#ffffff' }
};

return (
<ThemeContext.Provider value={{ theme, toggleTheme, styles:
themeStyles[theme] }}>
{children}
</ThemeContext.Provider>
);
}

function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}

function Header() {
const { theme, toggleTheme, styles } = useTheme();

return (
<header style={styles}>
<h1>My App</h1>
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode
</button>
</header>
);
}
```

3. PERFORMANCE OPTIMIZATION

Q: Explain React.memo, useMemo, and useCallback


A:
```jsx
import React, { useState, useMemo, useCallback, memo } from 'react';

// Memoized component
const ExpensiveComponent = memo(({ data, onItemClick }) => {
console.log('ExpensiveComponent rendered');
return (
<div>
{data.map(item => (
<div key={item.id} onClick={() => onItemClick(item.id)}>
{item.name} - {item.value}
</div>
))}
</div>
);
});

function PerformanceApp() {
const [users, setUsers] = useState([
{ id: 1, name: 'John', email: 'john@example.com' },
{ id: 2, name: 'Jane', email: 'jane@example.com' }
]);

const [count, setCount] = useState(0);

// Memoized expensive calculation


const expensiveData = useMemo(() => {
console.log('Computing expensive data...');
return users.map(user => ({
id: user.id,
name: user.name,
value: user.name.length * user.email.length
}));
}, [users]);

// Memoized callback
const handleItemClick = useCallback((itemId) => {
console.log('Item clicked:', itemId);
}, []);

return (
<div>
<button onClick={() => setCount(prev => prev + 1)}>
Count: {count}
</button>

<ExpensiveComponent
data={expensiveData}
onItemClick={handleItemClick}
/>
</div>
);
}
```

4. ADVANCED PATTERNS

Q: Implement Render Props pattern


A:
```jsx
function DataFetcher({ url, render }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

fetchData();
}, [url]);

return render({ data, loading, error });


}

// Usage
<DataFetcher
url="/api/users"
render={({ data, loading, error }) => {
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!data) return <div>No data</div>;

return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}}
/>
```

5. ERROR BOUNDARIES

Q: Implement an error boundary with retry functionality


A:
```jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
retryCount: 0
};
}

static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
}

handleRetry = () => {
this.setState(prev => ({
hasError: false,
error: null,
retryCount: prev.retryCount + 1
}));
};

render() {
if (this.state.hasError) {
return (
<div>
<h2>Something went wrong!</h2>
<p>We're sorry, but something unexpected happened.</p>

{this.state.retryCount < 3 && (


<button onClick={this.handleRetry}>
Try Again ({this.state.retryCount + 1}/3)
</button>
)}

<button onClick={() => window.location.reload()}>


Reload Page
</button>
</div>
);
}

return this.props.children;
}
}
```

6. TESTING REACT COMPONENTS

Q: How do you test React components?


A:
```jsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

// Component to test
function LoginForm({ onSubmit, loading = false }) {
const [formData, setFormData] = useState({ email: '', password: '' });
const [errors, setErrors] = useState({});

const handleSubmit = (e) => {


e.preventDefault();

const newErrors = {};


if (!formData.email) newErrors.email = 'Email is required';
if (!formData.password) newErrors.password = 'Password is required';

if (Object.keys(newErrors).length === 0) {
onSubmit(formData);
} else {
setErrors(newErrors);
}
};

return (
<form onSubmit={handleSubmit} data-testid="login-form">
<input
name="email"
value={formData.email}
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
data-testid="email-input"
/>
{errors.email && <span data-testid="email-error">{errors.email}</span>}

<input
name="password"
type="password"
value={formData.password}
onChange={(e) => setFormData(prev => ({ ...prev, password:
e.target.value }))}
data-testid="password-input"
/>
{errors.password && <span
data-testid="password-error">{errors.password}</span>}

<button type="submit" disabled={loading} data-testid="submit-button">


{loading ? 'Logging in...' : 'Login'}
</button>
</form>
);
}

// Test
describe('LoginForm', () => {
const mockOnSubmit = jest.fn();

test('shows validation errors for empty fields', async () => {


render(<LoginForm onSubmit={mockOnSubmit} />);

const submitButton = screen.getByTestId('submit-button');


fireEvent.click(submitButton);

await waitFor(() => {


expect(screen.getByTestId('email-error')).toHaveTextContent('Email is
required');
expect(screen.getByTestId('password-error')).toHaveTextContent('Password is
required');
});
});

test('submits form with valid data', async () => {


render(<LoginForm onSubmit={mockOnSubmit} />);

fireEvent.change(screen.getByTestId('email-input'), {
target: { value: 'test@example.com' }
});
fireEvent.change(screen.getByTestId('password-input'), {
target: { value: 'password123' }
});

fireEvent.click(screen.getByTestId('submit-button'));

await waitFor(() => {


expect(mockOnSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
});
});
});
});
```

7. PRACTICAL CODING EXERCISES

Q: Create a reusable Modal component with portal


A:
```jsx
import React, { useEffect } from 'react';
import { createPortal } from 'react-dom';

function Modal({ isOpen, onClose, title, children }) {


useEffect(() => {
const handleEscape = (e) => {
if (e.key === 'Escape') {
onClose();
}
};

if (isOpen) {
document.addEventListener('keydown', handleEscape);
document.body.style.overflow = 'hidden';
}

return () => {
document.removeEventListener('keydown', handleEscape);
document.body.style.overflow = 'unset';
};
}, [isOpen, onClose]);

if (!isOpen) return null;

return createPortal(
<div style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1000
}}>
<div style={{
backgroundColor: 'white',
padding: '2rem',
borderRadius: '8px',
maxWidth: '500px',
width: '90%'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems:
'center' }}>
<h2>{title}</h2>
<button onClick={onClose}>×</button>
</div>
{children}
</div>
</div>,
document.body
);
}

// Usage
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);

return (
<div>
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>

<Modal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Confirmation"
>
<p>Are you sure you want to proceed?</p>
<button onClick={() => setIsModalOpen(false)}>Cancel</button>
<button onClick={() => {
console.log('Confirmed');
setIsModalOpen(false);
}}>Confirm</button>
</Modal>
</div>
);
}
```

Q: Implement infinite scrolling with React


A:
```jsx
import React, { useState, useEffect, useRef, useCallback } from 'react';

function InfiniteScroll({ fetchData, renderItem }) {


const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const observer = useRef();

const lastItemRef = useCallback(node => {


if (loading) return;

if (observer.current) observer.current.disconnect();

observer.current = new IntersectionObserver(entries => {


if (entries[0].isIntersecting && hasMore) {
setPage(prevPage => prevPage + 1);
}
});

if (node) observer.current.observe(node);
}, [loading, hasMore]);

useEffect(() => {
const loadData = async () => {
setLoading(true);
try {
const newData = await fetchData(page);
if (newData.length === 0) {
setHasMore(false);
} else {
setItems(prev => [...prev, ...newData]);
}
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
}
};

loadData();
}, [page, fetchData]);

return (
<div>
{items.map((item, index) => {
if (items.length === index + 1) {
return (
<div key={item.id} ref={lastItemRef}>
{renderItem(item)}
</div>
);
} else {
return (
<div key={item.id}>
{renderItem(item)}
</div>
);
}
})}
{loading && <div>Loading...</div>}
{!hasMore && <div>No more items</div>}
</div>
);
}

// Usage
function App() {
const fetchData = async (page) => {
// Simulate API call
const response = await fetch(`/api/items?page=${page}&limit=10`);
return response.json();
};
const renderItem = (item) => (
<div style={{ padding: '1rem', border: '1px solid #ccc', margin: '0.5rem' }}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
);

return (
<InfiniteScroll fetchData={fetchData} renderItem={renderItem} />
);
}
```

COMMON INTERVIEW TIPS FOR 3 YEARS EXPERIENCE:


- Understand advanced hooks (useReducer, useMemo, useCallback)
- Know how to create and use custom hooks
- Be comfortable with Context API and state management
- Understand performance optimization techniques
- Know testing strategies and tools
- Be able to implement design patterns
- Understand error boundaries and error handling
- Know how to optimize component re-renders
- Be familiar with code splitting and lazy loading
- Understand the difference between controlled and uncontrolled components

You might also like