Frontend Interview Questions - Hard
Hard-level frontend interview questions covering advanced patterns, performance optimization, and complex architectures.
Q1: Explain React Fiber architecture and reconciliation.
Answer:
Fiber Architecture
Reconciliation Phases
Priority Levels
Concurrent Features
1import { useTransition, useDeferredValue } from 'react';
2
3function SearchResults() {
4 const [query, setQuery] = useState('');
5 const [isPending, startTransition] = useTransition();
6 const deferredQuery = useDeferredValue(query);
7
8 function handleChange(e) {
9 // Urgent: Update input immediately
10 setQuery(e.target.value);
11
12 // Non-urgent: Update results (can be interrupted)
13 startTransition(() => {
14 // This update has lower priority
15 // Can be interrupted by more urgent updates
16 });
17 }
18
19 return (
20 <div>
21 <input value={query} onChange={handleChange} />
22 {isPending && <Spinner />}
23 <Results query={deferredQuery} />
24 </div>
25 );
26}
Key Concepts:
- Time Slicing: Break work into chunks
- Suspense: Wait for async data
- Concurrent Rendering: Multiple versions of UI
- Automatic Batching: Batch multiple state updates
Q2: Implement a custom React hook for complex async state management.
Answer:
Advanced useAsync Hook
1import { useState, useEffect, useCallback, useRef } from 'react';
2
3function useAsync(asyncFunction, immediate = true) {
4 const [status, setStatus] = useState('idle');
5 const [data, setData] = useState(null);
6 const [error, setError] = useState(null);
7
8 // Memoize the async function
9 const execute = useCallback((...params) => {
10 setStatus('pending');
11 setData(null);
12 setError(null);
13
14 return asyncFunction(...params)
15 .then(response => {
16 setData(response);
17 setStatus('success');
18 return response;
19 })
20 .catch(error => {
21 setError(error);
22 setStatus('error');
23 throw error;
24 });
25 }, [asyncFunction]);
26
27 useEffect(() => {
28 if (immediate) {
29 execute();
30 }
31 }, [execute, immediate]);
32
33 return {
34 execute,
35 status,
36 data,
37 error,
38 isIdle: status === 'idle',
39 isPending: status === 'pending',
40 isSuccess: status === 'success',
41 isError: status === 'error'
42 };
43}
With Cancellation
1function useAsyncWithCancel(asyncFunction, immediate = true) {
2 const [status, setStatus] = useState('idle');
3 const [data, setData] = useState(null);
4 const [error, setError] = useState(null);
5 const cancelRef = useRef(false);
6
7 const execute = useCallback((...params) => {
8 cancelRef.current = false;
9 setStatus('pending');
10 setData(null);
11 setError(null);
12
13 return asyncFunction(...params)
14 .then(response => {
15 if (!cancelRef.current) {
16 setData(response);
17 setStatus('success');
18 }
19 return response;
20 })
21 .catch(error => {
22 if (!cancelRef.current) {
23 setError(error);
24 setStatus('error');
25 }
26 throw error;
27 });
28 }, [asyncFunction]);
29
30 const cancel = useCallback(() => {
31 cancelRef.current = true;
32 }, []);
33
34 useEffect(() => {
35 if (immediate) {
36 execute();
37 }
38 return cancel; // Cleanup on unmount
39 }, [execute, immediate, cancel]);
40
41 return { execute, cancel, status, data, error };
42}
With Retry and Cache
1function useAsyncAdvanced(asyncFunction, options = {}) {
2 const {
3 immediate = true,
4 retries = 3,
5 retryDelay = 1000,
6 cacheKey = null,
7 cacheTime = 5 * 60 * 1000 // 5 minutes
8 } = options;
9
10 const [status, setStatus] = useState('idle');
11 const [data, setData] = useState(null);
12 const [error, setError] = useState(null);
13 const [retryCount, setRetryCount] = useState(0);
14 const cacheRef = useRef(new Map());
15 const cancelRef = useRef(false);
16
17 const execute = useCallback(async (...params) => {
18 // Check cache
19 if (cacheKey) {
20 const cached = cacheRef.current.get(cacheKey);
21 if (cached && Date.now() - cached.timestamp < cacheTime) {
22 setData(cached.data);
23 setStatus('success');
24 return cached.data;
25 }
26 }
27
28 cancelRef.current = false;
29 setStatus('pending');
30 setError(null);
31
32 let attempt = 0;
33 while (attempt <= retries) {
34 try {
35 const response = await asyncFunction(...params);
36
37 if (cancelRef.current) return;
38
39 // Cache result
40 if (cacheKey) {
41 cacheRef.current.set(cacheKey, {
42 data: response,
43 timestamp: Date.now()
44 });
45 }
46
47 setData(response);
48 setStatus('success');
49 setRetryCount(0);
50 return response;
51 } catch (err) {
52 if (cancelRef.current) return;
53
54 attempt++;
55 if (attempt > retries) {
56 setError(err);
57 setStatus('error');
58 throw err;
59 }
60
61 setRetryCount(attempt);
62 await new Promise(resolve => setTimeout(resolve, retryDelay * attempt));
63 }
64 }
65 }, [asyncFunction, retries, retryDelay, cacheKey, cacheTime]);
66
67 const cancel = useCallback(() => {
68 cancelRef.current = true;
69 }, []);
70
71 const reset = useCallback(() => {
72 setStatus('idle');
73 setData(null);
74 setError(null);
75 setRetryCount(0);
76 }, []);
77
78 useEffect(() => {
79 if (immediate) {
80 execute();
81 }
82 return cancel;
83 }, [execute, immediate, cancel]);
84
85 return {
86 execute,
87 cancel,
88 reset,
89 status,
90 data,
91 error,
92 retryCount,
93 isIdle: status === 'idle',
94 isPending: status === 'pending',
95 isSuccess: status === 'success',
96 isError: status === 'error'
97 };
98}
Usage
1function UserProfile({ userId }) {
2 const fetchUser = useCallback(
3 () => fetch(`/api/users/${userId}`).then(r => r.json()),
4 [userId]
5 );
6
7 const { data, error, isPending, execute, retryCount } = useAsyncAdvanced(
8 fetchUser,
9 {
10 immediate: true,
11 retries: 3,
12 retryDelay: 1000,
13 cacheKey: `user-${userId}`,
14 cacheTime: 5 * 60 * 1000
15 }
16 );
17
18 if (isPending) return <div>Loading... {retryCount > 0 && `(Retry ${retryCount})`}</div>;
19 if (error) return <div>Error: {error.message} <button onClick={execute}>Retry</button></div>;
20 if (!data) return null;
21
22 return <div>{data.name}</div>;
23}
Q3: Explain Vue 3 reactivity system internals (Proxy-based).
Answer:
Reactivity Implementation
1// Simplified Vue 3 reactivity implementation
2
3let activeEffect = null;
4const targetMap = new WeakMap();
5
6function reactive(target) {
7 return new Proxy(target, {
8 get(target, key, receiver) {
9 // Track dependency
10 track(target, key);
11 return Reflect.get(target, key, receiver);
12 },
13 set(target, key, value, receiver) {
14 const result = Reflect.set(target, key, value, receiver);
15 // Trigger updates
16 trigger(target, key);
17 return result;
18 }
19 });
20}
21
22function track(target, key) {
23 if (!activeEffect) return;
24
25 let depsMap = targetMap.get(target);
26 if (!depsMap) {
27 targetMap.set(target, (depsMap = new Map()));
28 }
29
30 let dep = depsMap.get(key);
31 if (!dep) {
32 depsMap.set(key, (dep = new Set()));
33 }
34
35 dep.add(activeEffect);
36}
37
38function trigger(target, key) {
39 const depsMap = targetMap.get(target);
40 if (!depsMap) return;
41
42 const dep = depsMap.get(key);
43 if (dep) {
44 dep.forEach(effect => effect());
45 }
46}
47
48function effect(fn) {
49 activeEffect = fn;
50 fn();
51 activeEffect = null;
52}
Dependency Tracking
ref vs reactive
1import { ref, reactive, effect } from 'vue';
2
3// ref: For primitives
4const count = ref(0);
5console.log(count.value); // Access with .value
6
7effect(() => {
8 console.log('Count is:', count.value);
9});
10
11count.value++; // Triggers effect
12
13// reactive: For objects
14const state = reactive({
15 count: 0,
16 nested: {
17 value: 10
18 }
19});
20
21effect(() => {
22 console.log('State count:', state.count);
23 console.log('Nested value:', state.nested.value);
24});
25
26state.count++; // Triggers effect
27state.nested.value++; // Triggers effect (deep reactivity)
Computed Implementation
1function computed(getter) {
2 let value;
3 let dirty = true;
4
5 const effectFn = effect(getter, {
6 lazy: true,
7 scheduler() {
8 dirty = true;
9 }
10 });
11
12 return {
13 get value() {
14 if (dirty) {
15 value = effectFn();
16 dirty = false;
17 }
18 return value;
19 }
20 };
21}
Q4: Implement server-side rendering (SSR) with hydration.
Answer:
SSR Flow
React SSR Implementation
1// server.js
2import express from 'express';
3import React from 'react';
4import { renderToString } from 'react-dom/server';
5import App from './App';
6
7const app = express();
8
9app.get('*', (req, res) => {
10 const html = renderToString(<App url={req.url} />);
11
12 res.send(`
13 <!DOCTYPE html>
14 <html>
15 <head>
16 <title>SSR App</title>
17 <link rel="stylesheet" href="/styles.css">
18 </head>
19 <body>
20 <div id="root">${html}</div>
21 <script src="/bundle.js"></script>
22 </body>
23 </html>
24 `);
25});
26
27app.listen(3000);
1// client.js
2import React from 'react';
3import { hydrateRoot } from 'react-dom/client';
4import App from './App';
5
6// Hydrate instead of render
7hydrateRoot(
8 document.getElementById('root'),
9 <App />
10);
Data Fetching with SSR
1// App.js
2function App({ initialData }) {
3 const [data, setData] = useState(initialData);
4
5 useEffect(() => {
6 // Only fetch on client if no initial data
7 if (!initialData) {
8 fetchData().then(setData);
9 }
10 }, []);
11
12 return <div>{data?.title}</div>;
13}
14
15// server.js
16app.get('*', async (req, res) => {
17 // Fetch data on server
18 const initialData = await fetchData();
19
20 const html = renderToString(<App initialData={initialData} />);
21
22 res.send(`
23 <!DOCTYPE html>
24 <html>
25 <body>
26 <div id="root">${html}</div>
27 <script>
28 window.__INITIAL_DATA__ = ${JSON.stringify(initialData)};
29 </script>
30 <script src="/bundle.js"></script>
31 </body>
32 </html>
33 `);
34});
35
36// client.js
37hydrateRoot(
38 document.getElementById('root'),
39 <App initialData={window.__INITIAL_DATA__} />
40);
Hydration Mismatch
1// ❌ Causes hydration mismatch
2function Component() {
3 return <div>{new Date().toString()}</div>;
4}
5
6// ✅ Fix: Render on client only
7function Component() {
8 const [date, setDate] = useState(null);
9
10 useEffect(() => {
11 setDate(new Date().toString());
12 }, []);
13
14 return <div>{date || 'Loading...'}</div>;
15}
Q5: Implement optimistic UI updates with rollback.
Answer:
Optimistic Update Pattern
1import { useState, useCallback } from 'react';
2
3function useTodos() {
4 const [todos, setTodos] = useState([]);
5 const [history, setHistory] = useState([]);
6
7 const addTodo = useCallback(async (text) => {
8 const optimisticTodo = {
9 id: `temp-${Date.now()}`,
10 text,
11 completed: false,
12 optimistic: true
13 };
14
15 // Optimistic update
16 setTodos(prev => [...prev, optimisticTodo]);
17 setHistory(prev => [...prev, { action: 'add', todo: optimisticTodo }]);
18
19 try {
20 // API call
21 const response = await fetch('/api/todos', {
22 method: 'POST',
23 body: JSON.stringify({ text })
24 });
25 const savedTodo = await response.json();
26
27 // Replace optimistic with real
28 setTodos(prev => prev.map(t =>
29 t.id === optimisticTodo.id ? savedTodo : t
30 ));
31 } catch (error) {
32 // Rollback on error
33 setTodos(prev => prev.filter(t => t.id !== optimisticTodo.id));
34 setHistory(prev => prev.slice(0, -1));
35 throw error;
36 }
37 }, []);
38
39 const updateTodo = useCallback(async (id, updates) => {
40 const previousTodos = todos;
41
42 // Optimistic update
43 setTodos(prev => prev.map(t =>
44 t.id === id ? { ...t, ...updates } : t
45 ));
46
47 try {
48 await fetch(`/api/todos/${id}`, {
49 method: 'PATCH',
50 body: JSON.stringify(updates)
51 });
52 } catch (error) {
53 // Rollback
54 setTodos(previousTodos);
55 throw error;
56 }
57 }, [todos]);
58
59 const deleteTodo = useCallback(async (id) => {
60 const deletedTodo = todos.find(t => t.id === id);
61
62 // Optimistic delete
63 setTodos(prev => prev.filter(t => t.id !== id));
64
65 try {
66 await fetch(`/api/todos/${id}`, { method: 'DELETE' });
67 } catch (error) {
68 // Rollback
69 setTodos(prev => [...prev, deletedTodo]);
70 throw error;
71 }
72 }, [todos]);
73
74 return { todos, addTodo, updateTodo, deleteTodo };
75}
With React Query
1import { useMutation, useQueryClient } from '@tanstack/react-query';
2
3function useTodosOptimistic() {
4 const queryClient = useQueryClient();
5
6 const addTodoMutation = useMutation({
7 mutationFn: (newTodo) => fetch('/api/todos', {
8 method: 'POST',
9 body: JSON.stringify(newTodo)
10 }).then(r => r.json()),
11
12 onMutate: async (newTodo) => {
13 // Cancel outgoing refetches
14 await queryClient.cancelQueries(['todos']);
15
16 // Snapshot previous value
17 const previousTodos = queryClient.getQueryData(['todos']);
18
19 // Optimistically update
20 queryClient.setQueryData(['todos'], old => [
21 ...old,
22 { ...newTodo, id: `temp-${Date.now()}` }
23 ]);
24
25 // Return context with snapshot
26 return { previousTodos };
27 },
28
29 onError: (err, newTodo, context) => {
30 // Rollback to snapshot
31 queryClient.setQueryData(['todos'], context.previousTodos);
32 },
33
34 onSettled: () => {
35 // Refetch after error or success
36 queryClient.invalidateQueries(['todos']);
37 }
38 });
39
40 return addTodoMutation;
41}
Flow Diagram
Q6: Implement micro-frontend architecture.
Answer:
Architecture Patterns
Module Federation (Webpack 5)
1// shell/webpack.config.js
2module.exports = {
3 plugins: [
4 new ModuleFederationPlugin({
5 name: 'shell',
6 remotes: {
7 mfe1: 'mfe1@http://localhost:3001/remoteEntry.js',
8 mfe2: 'mfe2@http://localhost:3002/remoteEntry.js'
9 },
10 shared: {
11 react: { singleton: true },
12 'react-dom': { singleton: true }
13 }
14 })
15 ]
16};
17
18// mfe1/webpack.config.js
19module.exports = {
20 plugins: [
21 new ModuleFederationPlugin({
22 name: 'mfe1',
23 filename: 'remoteEntry.js',
24 exposes: {
25 './Dashboard': './src/Dashboard',
26 './Profile': './src/Profile'
27 },
28 shared: {
29 react: { singleton: true },
30 'react-dom': { singleton: true }
31 }
32 })
33 ]
34};
Shell Application
1// shell/App.js
2import React, { lazy, Suspense } from 'react';
3
4const Dashboard = lazy(() => import('mfe1/Dashboard'));
5const Profile = lazy(() => import('mfe1/Profile'));
6const Settings = lazy(() => import('mfe2/Settings'));
7
8function App() {
9 return (
10 <div>
11 <nav>
12 <Link to="/dashboard">Dashboard</Link>
13 <Link to="/profile">Profile</Link>
14 <Link to="/settings">Settings</Link>
15 </nav>
16
17 <Suspense fallback={<div>Loading...</div>}>
18 <Routes>
19 <Route path="/dashboard" element={<Dashboard />} />
20 <Route path="/profile" element={<Profile />} />
21 <Route path="/settings" element={<Settings />} />
22 </Routes>
23 </Suspense>
24 </div>
25 );
26}
Communication Between MFEs
1// Shared event bus
2class EventBus {
3 constructor() {
4 this.events = {};
5 }
6
7 on(event, callback) {
8 if (!this.events[event]) {
9 this.events[event] = [];
10 }
11 this.events[event].push(callback);
12 }
13
14 off(event, callback) {
15 if (this.events[event]) {
16 this.events[event] = this.events[event].filter(cb => cb !== callback);
17 }
18 }
19
20 emit(event, data) {
21 if (this.events[event]) {
22 this.events[event].forEach(callback => callback(data));
23 }
24 }
25}
26
27export const eventBus = new EventBus();
28
29// MFE 1
30eventBus.emit('user:login', { userId: 123 });
31
32// MFE 2
33eventBus.on('user:login', (data) => {
34 console.log('User logged in:', data.userId);
35});
Shared State
1// Shared store
2import { create } from 'zustand';
3
4export const useSharedStore = create((set) => ({
5 user: null,
6 setUser: (user) => set({ user }),
7
8 theme: 'light',
9 setTheme: (theme) => set({ theme })
10}));
11
12// Use in any MFE
13function MFE1Component() {
14 const { user, setUser } = useSharedStore();
15 return <div>{user?.name}</div>;
16}
Q7: Implement advanced performance optimization techniques.
Answer:
Virtual Scrolling
1import { useVirtualizer } from '@tanstack/react-virtual';
2
3function VirtualList({ items }) {
4 const parentRef = useRef();
5
6 const virtualizer = useVirtualizer({
7 count: items.length,
8 getScrollElement: () => parentRef.current,
9 estimateSize: () => 50, // Estimated item height
10 overscan: 5 // Render extra items for smooth scrolling
11 });
12
13 return (
14 <div
15 ref={parentRef}
16 style={{ height: '400px', overflow: 'auto' }}
17 >
18 <div
19 style={{
20 height: `${virtualizer.getTotalSize()}px`,
21 position: 'relative'
22 }}
23 >
24 {virtualizer.getVirtualItems().map(virtualRow => (
25 <div
26 key={virtualRow.index}
27 style={{
28 position: 'absolute',
29 top: 0,
30 left: 0,
31 width: '100%',
32 height: `${virtualRow.size}px`,
33 transform: `translateY(${virtualRow.start}px)`
34 }}
35 >
36 {items[virtualRow.index].name}
37 </div>
38 ))}
39 </div>
40 </div>
41 );
42}
Intersection Observer for Lazy Loading
1function LazyImage({ src, alt }) {
2 const [isLoaded, setIsLoaded] = useState(false);
3 const [isInView, setIsInView] = useState(false);
4 const imgRef = useRef();
5
6 useEffect(() => {
7 const observer = new IntersectionObserver(
8 ([entry]) => {
9 if (entry.isIntersecting) {
10 setIsInView(true);
11 observer.disconnect();
12 }
13 },
14 { rootMargin: '50px' } // Start loading 50px before visible
15 );
16
17 if (imgRef.current) {
18 observer.observe(imgRef.current);
19 }
20
21 return () => observer.disconnect();
22 }, []);
23
24 return (
25 <div ref={imgRef} style={{ minHeight: '200px' }}>
26 {isInView && (
27 <img
28 src={src}
29 alt={alt}
30 onLoad={() => setIsLoaded(true)}
31 style={{ opacity: isLoaded ? 1 : 0, transition: 'opacity 0.3s' }}
32 />
33 )}
34 </div>
35 );
36}
Web Workers for Heavy Computation
1// worker.js
2self.addEventListener('message', (e) => {
3 const { data, action } = e.data;
4
5 if (action === 'process') {
6 // Heavy computation
7 const result = data.map(item => {
8 // Complex processing
9 return processItem(item);
10 });
11
12 self.postMessage({ result });
13 }
14});
15
16// Component
17function HeavyComputation() {
18 const [result, setResult] = useState(null);
19 const workerRef = useRef();
20
21 useEffect(() => {
22 workerRef.current = new Worker('/worker.js');
23
24 workerRef.current.onmessage = (e) => {
25 setResult(e.data.result);
26 };
27
28 return () => workerRef.current.terminate();
29 }, []);
30
31 const handleProcess = (data) => {
32 workerRef.current.postMessage({ action: 'process', data });
33 };
34
35 return <div>{/* UI */}</div>;
36}
Request Deduplication
1const requestCache = new Map();
2
3function dedupeRequest(key, fetcher) {
4 if (requestCache.has(key)) {
5 return requestCache.get(key);
6 }
7
8 const promise = fetcher()
9 .then(data => {
10 requestCache.delete(key);
11 return data;
12 })
13 .catch(error => {
14 requestCache.delete(key);
15 throw error;
16 });
17
18 requestCache.set(key, promise);
19 return promise;
20}
21
22// Usage
23function useUser(userId) {
24 return useQuery(['user', userId], () =>
25 dedupeRequest(`user-${userId}`, () =>
26 fetch(`/api/users/${userId}`).then(r => r.json())
27 )
28 );
29}
Q8: Implement advanced form handling with validation.
Answer:
Custom useForm Hook
1import { useState, useCallback } from 'react';
2
3function useForm(initialValues, validationSchema) {
4 const [values, setValues] = useState(initialValues);
5 const [errors, setErrors] = useState({});
6 const [touched, setTouched] = useState({});
7 const [isSubmitting, setIsSubmitting] = useState(false);
8
9 const validate = useCallback((fieldName, value) => {
10 if (!validationSchema[fieldName]) return null;
11
12 const rules = validationSchema[fieldName];
13
14 for (const rule of rules) {
15 const error = rule(value, values);
16 if (error) return error;
17 }
18
19 return null;
20 }, [validationSchema, values]);
21
22 const validateAll = useCallback(() => {
23 const newErrors = {};
24
25 Object.keys(validationSchema).forEach(fieldName => {
26 const error = validate(fieldName, values[fieldName]);
27 if (error) {
28 newErrors[fieldName] = error;
29 }
30 });
31
32 setErrors(newErrors);
33 return Object.keys(newErrors).length === 0;
34 }, [validate, validationSchema, values]);
35
36 const handleChange = useCallback((name, value) => {
37 setValues(prev => ({ ...prev, [name]: value }));
38
39 // Validate on change if already touched
40 if (touched[name]) {
41 const error = validate(name, value);
42 setErrors(prev => ({
43 ...prev,
44 [name]: error
45 }));
46 }
47 }, [touched, validate]);
48
49 const handleBlur = useCallback((name) => {
50 setTouched(prev => ({ ...prev, [name]: true }));
51
52 const error = validate(name, values[name]);
53 setErrors(prev => ({
54 ...prev,
55 [name]: error
56 }));
57 }, [validate, values]);
58
59 const handleSubmit = useCallback(async (onSubmit) => {
60 setIsSubmitting(true);
61
62 const isValid = validateAll();
63 if (!isValid) {
64 setIsSubmitting(false);
65 return;
66 }
67
68 try {
69 await onSubmit(values);
70 } catch (error) {
71 console.error(error);
72 } finally {
73 setIsSubmitting(false);
74 }
75 }, [validateAll, values]);
76
77 const reset = useCallback(() => {
78 setValues(initialValues);
79 setErrors({});
80 setTouched({});
81 }, [initialValues]);
82
83 return {
84 values,
85 errors,
86 touched,
87 isSubmitting,
88 handleChange,
89 handleBlur,
90 handleSubmit,
91 reset
92 };
93}
Validation Rules
1const required = (message = 'Required') => (value) => {
2 return value ? null : message;
3};
4
5const minLength = (min, message) => (value) => {
6 return value && value.length >= min ? null : message || `Min length: ${min}`;
7};
8
9const email = (message = 'Invalid email') => (value) => {
10 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
11 return emailRegex.test(value) ? null : message;
12};
13
14const match = (fieldName, message) => (value, allValues) => {
15 return value === allValues[fieldName] ? null : message;
16};
17
18// Usage
19const validationSchema = {
20 email: [required(), email()],
21 password: [required(), minLength(8, 'Password must be at least 8 characters')],
22 confirmPassword: [
23 required(),
24 match('password', 'Passwords must match')
25 ]
26};
27
28function SignupForm() {
29 const form = useForm(
30 { email: '', password: '', confirmPassword: '' },
31 validationSchema
32 );
33
34 return (
35 <form onSubmit={(e) => {
36 e.preventDefault();
37 form.handleSubmit(async (values) => {
38 await fetch('/api/signup', {
39 method: 'POST',
40 body: JSON.stringify(values)
41 });
42 });
43 }}>
44 <input
45 name="email"
46 value={form.values.email}
47 onChange={(e) => form.handleChange('email', e.target.value)}
48 onBlur={() => form.handleBlur('email')}
49 />
50 {form.touched.email && form.errors.email && (
51 <span>{form.errors.email}</span>
52 )}
53
54 {/* Other fields... */}
55
56 <button type="submit" disabled={form.isSubmitting}>
57 {form.isSubmitting ? 'Submitting...' : 'Submit'}
58 </button>
59 </form>
60 );
61}
Q9: Implement advanced state machines with XState.
Answer:
Authentication State Machine
1import { createMachine, interpret } from 'xstate';
2
3const authMachine = createMachine({
4 id: 'auth',
5 initial: 'idle',
6 context: {
7 user: null,
8 error: null,
9 retries: 0
10 },
11 states: {
12 idle: {
13 on: {
14 LOGIN: 'loading'
15 }
16 },
17 loading: {
18 invoke: {
19 src: 'loginUser',
20 onDone: {
21 target: 'authenticated',
22 actions: 'setUser'
23 },
24 onError: [
25 {
26 target: 'error',
27 cond: 'maxRetriesReached',
28 actions: 'setError'
29 },
30 {
31 target: 'loading',
32 actions: 'incrementRetries'
33 }
34 ]
35 }
36 },
37 authenticated: {
38 on: {
39 LOGOUT: {
40 target: 'idle',
41 actions: 'clearUser'
42 },
43 REFRESH: 'refreshing'
44 }
45 },
46 refreshing: {
47 invoke: {
48 src: 'refreshToken',
49 onDone: {
50 target: 'authenticated',
51 actions: 'setUser'
52 },
53 onError: {
54 target: 'idle',
55 actions: 'clearUser'
56 }
57 }
58 },
59 error: {
60 on: {
61 RETRY: {
62 target: 'loading',
63 actions: 'resetRetries'
64 },
65 CANCEL: 'idle'
66 }
67 }
68 }
69}, {
70 actions: {
71 setUser: (context, event) => {
72 context.user = event.data;
73 },
74 clearUser: (context) => {
75 context.user = null;
76 },
77 setError: (context, event) => {
78 context.error = event.data;
79 },
80 incrementRetries: (context) => {
81 context.retries++;
82 },
83 resetRetries: (context) => {
84 context.retries = 0;
85 }
86 },
87 guards: {
88 maxRetriesReached: (context) => context.retries >= 3
89 },
90 services: {
91 loginUser: async (context, event) => {
92 const response = await fetch('/api/login', {
93 method: 'POST',
94 body: JSON.stringify(event.credentials)
95 });
96 return response.json();
97 },
98 refreshToken: async () => {
99 const response = await fetch('/api/refresh');
100 return response.json();
101 }
102 }
103});
Using with React
1import { useMachine } from '@xstate/react';
2
3function AuthComponent() {
4 const [state, send] = useMachine(authMachine);
5
6 const handleLogin = (credentials) => {
7 send({ type: 'LOGIN', credentials });
8 };
9
10 return (
11 <div>
12 {state.matches('idle') && (
13 <LoginForm onSubmit={handleLogin} />
14 )}
15
16 {state.matches('loading') && (
17 <div>Loading... (Attempt {state.context.retries + 1})</div>
18 )}
19
20 {state.matches('authenticated') && (
21 <div>
22 Welcome, {state.context.user.name}!
23 <button onClick={() => send('LOGOUT')}>Logout</button>
24 </div>
25 )}
26
27 {state.matches('error') && (
28 <div>
29 Error: {state.context.error}
30 <button onClick={() => send('RETRY')}>Retry</button>
31 <button onClick={() => send('CANCEL')}>Cancel</button>
32 </div>
33 )}
34 </div>
35 );
36}
Q10: Implement real-time collaborative editing.
Answer:
Operational Transformation
1// Simple OT implementation
2class Operation {
3 constructor(type, position, content) {
4 this.type = type; // 'insert' or 'delete'
5 this.position = position;
6 this.content = content;
7 }
8
9 transform(other) {
10 if (this.type === 'insert' && other.type === 'insert') {
11 if (this.position < other.position) {
12 return other;
13 } else {
14 return new Operation(
15 other.type,
16 other.position + this.content.length,
17 other.content
18 );
19 }
20 }
21
22 if (this.type === 'delete' && other.type === 'insert') {
23 if (this.position < other.position) {
24 return new Operation(
25 other.type,
26 other.position - this.content.length,
27 other.content
28 );
29 }
30 return other;
31 }
32
33 // More transformation rules...
34 return other;
35 }
36}
37
38function useCollaborativeEditor(documentId) {
39 const [content, setContent] = useState('');
40 const [pendingOps, setPendingOps] = useState([]);
41 const wsRef = useRef();
42
43 useEffect(() => {
44 wsRef.current = new WebSocket(`ws://localhost:3000/doc/${documentId}`);
45
46 wsRef.current.onmessage = (event) => {
47 const { type, operation } = JSON.parse(event.data);
48
49 if (type === 'operation') {
50 // Transform against pending operations
51 let transformedOp = operation;
52 pendingOps.forEach(pending => {
53 transformedOp = pending.transform(transformedOp);
54 });
55
56 // Apply operation
57 applyOperation(transformedOp);
58 }
59 };
60
61 return () => wsRef.current.close();
62 }, [documentId]);
63
64 const handleChange = (newContent) => {
65 const op = calculateOperation(content, newContent);
66
67 // Apply locally
68 setContent(newContent);
69
70 // Send to server
71 wsRef.current.send(JSON.stringify({
72 type: 'operation',
73 operation: op
74 }));
75
76 // Track pending
77 setPendingOps(prev => [...prev, op]);
78 };
79
80 return { content, handleChange };
81}
CRDT Implementation
1// Simplified CRDT for text editing
2class CRDT {
3 constructor() {
4 this.chars = [];
5 this.siteId = Math.random().toString(36);
6 this.clock = 0;
7 }
8
9 insert(position, char) {
10 const id = {
11 site: this.siteId,
12 clock: this.clock++,
13 position
14 };
15
16 this.chars.splice(position, 0, { id, char });
17 return { type: 'insert', id, char };
18 }
19
20 delete(position) {
21 const deleted = this.chars.splice(position, 1)[0];
22 return { type: 'delete', id: deleted.id };
23 }
24
25 applyRemote(operation) {
26 if (operation.type === 'insert') {
27 const position = this.findPosition(operation.id);
28 this.chars.splice(position, 0, {
29 id: operation.id,
30 char: operation.char
31 });
32 } else if (operation.type === 'delete') {
33 const position = this.chars.findIndex(c =>
34 c.id.site === operation.id.site &&
35 c.id.clock === operation.id.clock
36 );
37 if (position !== -1) {
38 this.chars.splice(position, 1);
39 }
40 }
41 }
42
43 toString() {
44 return this.chars.map(c => c.char).join('');
45 }
46}
Summary
Hard frontend topics:
- React Fiber: Incremental rendering, time slicing, concurrent features
- Custom Hooks: Advanced async state management with retry and caching
- Vue 3 Reactivity: Proxy-based reactivity, effect system
- SSR & Hydration: Server-side rendering, data fetching, hydration mismatch
- Optimistic UI: Immediate updates with rollback on failure
- Micro-Frontends: Module federation, independent deployment
- Performance: Virtual scrolling, lazy loading, web workers, deduplication
- Advanced Forms: Custom form hook, validation, error handling
- State Machines: XState, finite states, transitions, guards
- Collaborative Editing: Operational transformation, CRDTs, real-time sync
These advanced concepts enable building production-grade, scalable frontend applications.
Related Snippets
- Frontend Interview Questions - Easy
Easy-level frontend interview questions covering HTML, CSS, JavaScript, React, … - Frontend Interview Questions - Medium
Medium-level frontend interview questions covering advanced React, Vue, state …