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