- React
- Form Architecture, Modern FormData, and Validation
- Complex Input Patterns: Files & Input Masking
Complex Input Patterns: Files & Input Masking
Complex inputs require specialized DOM handling: file inputs (<input type="file">) are strictly uncontrolled and managed via refs and FormData, while masked inputs (phone numbers, credit cards) require tracking cursor selection ranges to prevent the caret from jumping during format insertion.
Key Learning Objectives
Manage single and multi-file uploads accessibly with drag-and-drop and progress states.
Implement phone number and credit card masked inputs that preserve cursor position.
Handle file size, MIME type validation, and preview generation using URL.createObjectURL.
Clean up object URLs on component unmount to prevent memory leaks.
The Interview Problem & Context
Implement a masked phone number input component (format: (XXX) XXX-XXXX) that formats numbers as the user types while preserving the caret cursor position.
1import React, { useRef, useState, useLayoutEffect } from 'react';23function formatPhone(digits: string): string {4 const clean = digits.replace(/\D/g, '').slice(0, 10);5 if (clean.length <= 3) return clean;6 if (clean.length <= 6) return `(${clean.slice(0, 3)}) ${clean.slice(3)}`;7 return `(${clean.slice(0, 3)}) ${clean.slice(3, 6)}-${clean.slice(6)}`;8}910export function MaskedPhoneInput() {11 const [value, setValue] = useState('');12 const inputRef = useRef<HTMLInputElement>(null);13 const cursorRef = useRef<number | null>(null);1415 function handleChange(e: React.ChangeEvent<HTMLInputElement>) {16 const input = e.target;17 cursorRef.current = input.selectionStart; // Record cursor position before format18 setValue(formatPhone(input.value));19 }2021 useLayoutEffect(() => {22 // Restore cursor position synchronously before paint23 if (inputRef.current && cursorRef.current !== null) {24 inputRef.current.setSelectionRange(cursorRef.current, cursorRef.current);25 }26 }, [value]);2728 return (29 <input30 ref={inputRef}31 type="tel"32 value={value}33 onChange={handleChange}34 placeholder="(555) 000-0000"35 />36 );37}
Deep Architectural Explanation
Handling Complex Input Patterns
1. Input Masking & Caret Preservation
When formatting strings (adding parentheses, spaces, dashes):
- Programmatically setting
input.valueforces the browser caret to the end of the input. - Solution:
- Record
input.selectionStartbefore state update. - Apply the format transformation.
- Inside
useLayoutEffect(synchronously before paint), callinput.setSelectionRange(pos, pos). - The user sees seamless formatting without the cursor jumping away from their active editing position!
- Record
2. File Uploads & Memory Safety
File inputs cannot be controlled (value cannot be set programmatically):
<input type="file" ref={fileInputRef} onChange={handleFile} />- When displaying previews of selected images:
const previewUrl = URL.createObjectURL(file);- Memory Leak Alert: Browser memory keeps this blob reference alive until the tab closes. Always revoke it in
useEffectcleanup:
return () => URL.revokeObjectURL(previewUrl);Common Interview Traps & Pitfalls
Using useEffect to restore cursor position, causing visible cursor jumping and visual jitter.
Creating object URLs with URL.createObjectURL without revoking them, causing browser memory leaks.
Attempting to pass value={fileState} to a file input, which throws a DOM security error.
Senior Interviewer Follow-Up Probes
How do you calculate the new cursor position when formatting characters (like parentheses) are inserted before the cursor?
How do you handle drag-and-drop file zones with onDragOver and onDrop in React?
How do you track upload progress percentages with XMLHttpRequest or fetch streams?
Frequently Asked Interview Questions
Why must you use useLayoutEffect when restoring caret position in masked inputs?
useLayoutEffect runs synchronously before browser paint, ensuring the cursor position is restored before the frame renders so the user never sees the caret jump.
How do you preview an uploaded image in React?
Use URL.createObjectURL(file) to generate a temporary local blob URL to pass to an <img src="..." /> tag.
Why is calling URL.revokeObjectURL essential in cleanup?
Each call to createObjectURL holds the file in browser memory until explicitly released. Revoking it prevents memory leaks when uploading large images.
What is input masking?
A technique that automatically formats user input into an expected visual pattern (like phone numbers, dates, or credit card numbers) as they type.
