Tổng hợp 202 câu hỏi phỏng vấn React kèm đáp án chi tiết, song ngữ Việt–Anh — sắp xếp từ cơ bản đến nâng cao. Ôn luyện miễn phí, tự động lưu tiến độ.
JSX là cú pháp cho phép viết thẻ giống HTML ngay trong file JavaScript. Nó không phải là một ngôn ngữ riêng — trình duyệt hoàn toàn không hiểu JSX, mà Babel hoặc SWC dịch nó thành lời gọi hàm trước khi code chạy. Hình…
JSX được compiler dịch thành lời gọi hàm tạo element — React.createElement(type, props, ...children) ở bản cũ, và jsx() từ react/jsx-runtime kể từ React 17. Kết quả trả về không phải DOM node, mà là một object mô tả thuần: { type: 'h1', props: {…
Conditional rendering trong React không có cú pháp riêng — bạn dùng đúng biểu thức JavaScript bình thường. Vì JSX chỉ là lời gọi hàm, mọi thứ trả về giá trị đều nhúng được vào ngoặc nhọn. Ba cách hay dùng, theo thứ tự từ…
Fragment cho phép một component trả về nhiều phần tử cùng cấp mà không thêm thẻ bọc nào vào DOM. Viết đầy đủ là <React.Fragment, viết tắt là <...</. Hai chỗ Fragment không chỉ là dọn dẹp mà là bắt buộc: - HTML có ràng…
key là cách bạn nói cho React biết phần tử nào trong danh sách là phần tử nào giữa hai lần render. Không có nó, React chỉ còn vị trí trong mảng để so — và vị trí thì thay đổi mỗi khi bạn chèn,…
Biểu thức (expression) là đoạn code trả về một giá trị, còn câu lệnh (statement) là đoạn code thực thi một hành động nhưng không trả về giá trị. Trong cặp dấu {} của JSX, bạn chỉ có thể đặt biểu thức, không thể đặt…
Khác ba chỗ: tên viết camelCase, giá trị là tham chiếu hàm chứ không phải chuỗi, và không có return false. Bên dưới còn một khác biệt lớn hơn ba cái đó — React không gắn listener lên từng thẻ. Cặp ngoặc cuối là lỗi…
Spread operator trong JSX là cú pháp {...object} để bung tất cả key-value của một object ra thành các props riêng lẻ truyền vào component. Khi viết <Button {...btnProps} /, JSX compiler chuyển thành React.createElement(Button, btnProps). Nếu có key trùng, props viết trực tiếp trên…
React bỏ qua null, undefined, true và false; nhưng số 0 và chuỗi thì render ra màn hình. Chuỗi rỗng cũng không hiện gì, nhưng nó không nằm trong nhóm bị bỏ qua — nó vẫn là một giá trị React đem đi render, chỉ…
Vì JSX dịch thành lời gọi hàm, và một hàm JavaScript chỉ trả về được một giá trị. Ràng buộc này không phải luật riêng của React — nó là ràng buộc của chính ngôn ngữ. Đoạn thứ hai là lỗi cú pháp JavaScript thuần,…
props.children chứa mọi thứ bạn đặt giữa thẻ mở và thẻ đóng của một component. Nó là prop bình thường, chỉ khác ở chỗ JSX tự điền giá trị cho nó thay vì bạn viết tay. Hai đoạn dưới đây tương đương nhau — cho…
Thay bằng React.memo, nhưng hai thứ không tương đương hoàn toàn: PureComponent so cả props lẫn state, còn memo chỉ so props. PureComponent là class kế thừa thay cho Component, tự cài sẵn shouldComponentUpdate bằng phép so sánh nông trên this.props và this.state. memo là…
Controlled Component (Component được kiểm soát) là component mà giá trị của các form element (như <input, <textarea, <select) được kiểm soát hoàn toàn bởi React state. - Thay vì để DOM tự quản lý trạng thái, React state sẽ là source of truth duy…
Component Composition là kỹ thuật xây dựng các component phức tạp bằng cách "lắp ráp" hoặc "chứa" các component đơn giản hơn (giống như Lego), thay vì dùng cơ chế kế thừa (inheritance) của Lập trình Hướng đối tượng (OOP). Tại sao React khuyến khích…
Stateless component là component không quản lý state nội bộ; nó chỉ nhận props và render UI dựa trên props đó. Stateful component là component có state nội bộ, thường dùng để lưu dữ liệu thay đổi theo thời gian hoặc tương tác người dùng.…
Compound Components là pattern trong React cho phép bạn xây dựng một nhóm component phối hợp với nhau để tạo thành một UI duy nhất, trong đó component cha nắm giữ state và logic, còn các component con chia sẻ state đó một cách ngầm…
Trong vòng đời (lifecycle) của một component React, có 3 giai đoạn chính: 1. Mount (Gắn kết): Là khoảnh khắc component được tạo ra và chèn vào DOM thật lần đầu tiên. - Hook tương ứng: useEffect với dependency array rỗng []. 2. Update (Cập…
Component chỉ cần trả về một giá trị React biết cách render — không nhất thiết là JSX element. Từ React 16 trở đi, danh sách này rộng hơn nhiều người tưởng. Các kiểu trả về hợp lệ: - null, undefined, false, true — React…
Props là dữ liệu component nhận từ bên ngoài và không được sửa; state là dữ liệu component tự giữ và tự đổi. Ranh giới nằm ở chỗ ai sở hữu giá trị đó. Hình dung một hàm: props là tham số truyền vào, state…
Lifting state up là chuyển state lên component cha chung gần nhất khi có từ hai component trở lên cần cùng một dữ liệu. Vì dữ liệu trong React chảy một chiều từ trên xuống, hai component anh em không nói chuyện trực tiếp được…
Bất biến ở đây nghĩa là không sửa object hay array đang nằm trong state, mà tạo bản mới rồi đưa vào setter. Lý do rất cụ thể: React quyết định có render lại hay không bằng Object.is trên giá trị cũ và mới —…
Phải tạo bản sao mới ở mọi cấp nằm trên đường đi tới giá trị cần đổi. React so state cũ và mới bằng Object.is, tức so tham chiếu — sửa tại chỗ rồi gọi setter thì React thấy "vẫn là object cũ" và bỏ…
Derived state là dữ liệu được tính toán từ props hoặc state đã có, không phải dữ liệu lưu trữ riêng. Thay vì copy props vào useState, bạn tính giá trị mỗi lần render. Vấn đề thường gặp nhất là đồng bộ state với props:…
Unidirectional Data Flow (Luồng dữ liệu một chiều) trong React có nghĩa là: - Dữ liệu luôn chảy theo một hướng duy nhất: Từ Component cha xuống Component con (thông qua props). - Component con không bao giờ được phép thay đổi trực tiếp props…
Gần nhất là useEffect(() = { ... }, []), nhưng hai thứ không trùng khít: thời điểm chạy khác nhau, và trong Strict Mode ở môi trường phát triển thì số lần chạy cũng khác. componentDidMount chạy đúng một lần, ngay sau khi component được…
componentDidUpdate(prevProps, prevState) được gọi sau mỗi lần re-render (trừ lần đầu mount). So sánh prevProps/prevState với giá trị hiện tại để quyết định có nên thực hiện side effects không. Luôn wrap code trong điều kiện if (prevProps.id !== this.props.id) để tránh infinite loop.
Tương đương là hàm cleanup trả về từ useEffect — nhưng nó chạy nhiều hơn componentWillUnmount: trước mỗi lần effect chạy lại, chứ không chỉ lúc unmount. Khác biệt này là điểm cộng chứ không phải phiền toái: nó ép bạn viết phần huỷ ngay…
componentDidCatch(error, info) là lifecycle method cho phép Class Component bắt lỗi từ component con trong render phase. Kết hợp với getDerivedStateFromError để tạo Error Boundary - component bắt lỗi và hiển thị fallback UI. Function components không thể là Error Boundary, phải dùng class hoặc…
Thứ tự lifecycle methods qua 3 giai đoạn: - Mount: constructor → getDerivedStateFromProps → render → DOM update → componentDidMount. - Update: getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → DOM update → componentDidUpdate. - Unmount: componentWillUnmount → component bị xoá. - Với concurrent mode,…
useState trả về một cặp: giá trị hiện tại và hàm để đổi nó. Gọi hàm setter không sửa biến tại chỗ — nó lên lịch cho React render lại component với giá trị mới. Điểm khiến người mới vấp: count là hằng số trong…
Dependency array là danh sách những giá trị mà effect phụ thuộc vào; React chạy lại effect khi một trong số đó đổi. Nó không phải bộ hẹn giờ hay công tắc — nó là lời khai báo "effect này đọc những thứ này". Ba…
Cleanup là hàm bạn trả về từ useEffect; React gọi nó trước mỗi lần chạy lại effect và một lần nữa khi component unmount. Nó không phải "hàm dọn dẹp lúc thoát" — nó chạy nhiều lần trong đời một component. Nếu delay đổi, React…
Vì useEffect dùng giá trị trả về của callback làm hàm cleanup, mà hàm async luôn trả về một Promise. React sẽ cầm Promise đó và cố gọi nó như một hàm khi dọn dẹp. Phần AbortController không phải trang trí. Effect có thể chạy…
Vòng lặp vô hạn xảy ra khi effect cập nhật chính thứ mà nó phụ thuộc vào. Set state → render lại → dependency đổi → effect chạy → set state. Chuỗi này không tự dừng. Ba nguyên nhân phổ biến và cách chữa tương…
Stale closure là khi một hàm vẫn nhìn thấy giá trị của lần render đã cũ. Nó không phải lỗi của React mà là hệ quả bình thường của closure trong JavaScript: mỗi lần render tạo ra một bộ biến mới, và hàm nào được…
Trong Strict Mode ở môi trường phát triển, React 18 cố ý chạy effect hai lần: mount → cleanup → mount lại. Đây không phải lỗi và cũng không xảy ra ở production — nó là bài kiểm tra xem effect của bạn có dọn…
Không. Mỗi component gọi hook nhận một bản state riêng — custom hook chia sẻ logic, không chia sẻ dữ liệu. Đây là chỗ tên gọi gây hiểu nhầm. "Share stateful logic" nghe như hai component sẽ thấy cùng một giá trị, nhưng custom hook…
Race condition xảy ra khi effect chạy lại trước lúc request cũ trả về — hai response về không đúng thứ tự và bản cũ ghi đè bản mới. Không có gì báo lỗi; giao diện chỉ đơn giản hiện sai dữ liệu. Hình dung…
useContext là hook giúp component đọc giá trị từ React Context mà không cần truyền props qua từng cấp. Bạn truyền vào object tạo bởi createContext, hook trả về value của Provider gần nhất phía trên trong cây. Hãy hình dung Context như bảng thông…
useMemo memoize kết quả tính toán tốn kém, chỉ tính lại khi dependencies thay đổi. Dùng khi có tính toán expensive mà không muốn chạy lại mỗi render, hoặc cần stable object reference. Không nên dùng quá mức vì có overhead riêng.
useCallback memoize function để giữ stable reference giữa renders. Dùng khi truyền callback xuống child component được wrap bởi React.memo, hoặc khi function là dependency của useEffect.
useRef tạo một mutable object có property .current sống xuyên suốt vòng đời component, và thay đổi .current không trigger re-render. Ví von: useRef giống một chiếc hộp nhỏ bạn gắn vào component. Bạn mở hộp ra, bỏ gì vào cũng được, lấy ra bất…
Hai quy tắc: chỉ gọi hook ở cấp cao nhất của hàm, và chỉ gọi trong function component hoặc custom hook. Cả hai đều xuất phát từ một chi tiết cài đặt — React nhận diện hook theo thứ tự gọi, không theo tên. React…
Mọi component gọi useContext đều render lại khi giá trị của Provider đổi — kể cả khi nó chỉ đọc một field không liên quan. Context không có cơ chế đăng ký theo từng phần; nó so sánh cả object bằng Object.is. Lỗi phổ biến…
useMemo là hook memoize giá trị/kết quả tính toán bên trong component. React.memo là HOC memoize toàn bộ component, skip re-render khi props không thay đổi (shallow comparison). Kết hợp cả hai: dùng React.memo cho component, useCallback cho callback props để React.memo hoạt động hiệu…
Custom hook là JavaScript function bắt đầu bằng 'use' có thể gọi các hooks khác. Dùng để tái sử dụng stateful logic giữa components. Mỗi component sử dụng custom hook có state instance riêng biệt, không chia sẻ.
Dùng custom hook khi logic cần: React hooks (useState, useEffect, v.v.), React lifecycle, hay stateful behavior. Dùng utility function (pure function) khi: chỉ xử lý data transformation, validation, formatting - không cần React context. Custom hook gọi được từ components, utility function gọi từ bất…
useDebounce delay việc cập nhật value cho đến khi user ngừng thay đổi trong khoảng thời gian nhất định:
usePrevious lưu giá trị trước đó của một value sử dụng useRef. Sau mỗi render, ref được cập nhật với value hiện tại nhưng function trả về ref.current (value từ render trước).
Context API cho phép chia sẻ dữ liệu (theme, language, auth, user) qua component tree mà không cần pass props qua từng cấp trung gian (props drilling). Gồm ba phần: createContext, Provider, useContext.
Dùng Context khi: dữ liệu cần ở nhiều levels sâu (theme, locale, auth), data được nhiều components dùng, props chỉ passed qua để forward (không dùng ở cấp trung gian). Không nên dùng cho state thay đổi thường xuyên vì gây nhiều re-renders. Props drilling…
useParams trả về object chứa các dynamic segment của URL route đang active. Ví dụ route /users/:id khi truy cập /users/42 thì const { id } = useParams() trả về id = '42'. Lưu ý giá trị luôn là string, cần tự convert nếu cần…
useNavigate trả về navigate function để programmatically điều hướng: navigate('/path'), navigate(-1) (back), navigate('/path', { replace: true }). Thay thế cho useHistory trong v5. Dùng khi cần navigate sau form submit, sau auth login, hay trong event handlers không phải JSX.
Nested route dùng component <Outlet / làm chỗ cắm cho route con. Route cha render layout dùng chung, <Outlet / đánh dấu vị trí nội dung con hiện ra. Đường dẫn của route con là tương đối — không phải lặp lại path của route…
Tạo một component bọc, kiểm tra auth rồi redirect nếu chưa đăng nhập: Truyền state={{ from: location }} để sau khi đăng nhập xong còn đưa user về đúng trang họ định vào. <Navigate replace là bản thay thế cho <Redirect của React Router v5.
Tổ chức nhiều context bằng cách: tạo nhiều Provider riêng biệt, nest chúng lại (thứ tự từ ngoài vào trong), hoặc tạo AppProviders component bọc tất cả providers. Mỗi context chịu trách nhiệm một concern (AuthContext, ThemeContext, CartContext). Tránh god context chứa tất cả global…
Dùng useSearchParams hook: const [searchParams, setSearchParams] = useSearchParams(). Đọc: searchParams.get('tab'). Cập nhật: setSearchParams({ tab: 'new' }) hay setSearchParams(prev = { prev.set('tab', 'new'); return prev; }). Phù hợp cho filter, pagination state cần share qua URL.
Uncontrolled component lưu giá trị trong DOM, không sync với React state. Dùng ref để đọc khi cần (submit): const inputRef = useRef(null); <input ref={inputRef} /. Kể từ React 19, useRef yêu cầu truyền giá trị khởi tạo (thường là null cho DOM refs). Đơn…
Formik là thư viện quản lý form state, validation, và submission trong React. Xử lý: touched/dirty states, error messages, submission handling, field-level và form-level validation. Giảm boilerplate code đáng kể so với quản lý state thủ công. Tích hợp tốt với Yup cho schema validation.
React Hook Form dùng uncontrolled components và refs thay vì state, nên ít re-renders hơn (performance tốt hơn). API đơn giản hơn với register, handleSubmit, formState. Bundle size nhỏ hơn Formik. Formik dễ học hơn với explicit controlled pattern. RHF được cộng đồng ưa chuộng…
Các cách: - (1) Manual: check trong onChange/onSubmit, lưu errors vào state. - (2) HTML5 built-in: required, minLength, pattern attributes. - (3) Formik + Yup: declarative schema validation. - (4) React Hook Form với resolver (Yup, Zod). Dùng Zod cho type-safe validation kết hợp TypeScript…
Error Boundary là React component bắt JavaScript errors trong component tree con và hiển thị fallback UI thay vì crash toàn app. Chỉ có thể tạo với Class Component implement getDerivedStateFromError (set error state) và componentDidCatch (log error). Thư viện react-error-boundary cung cấp ErrorBoundary component…
Error Boundary không bắt: errors trong event handlers (phải try/catch thủ công), async code (setTimeout, Promises), server-side rendering, errors trong chính Error Boundary component. Chỉ bắt errors trong render phase, lifecycle methods, constructors của component tree bên dưới.
Lưu array of field objects trong state. Render với map, mỗi field có unique id làm key. Thêm: setFields(prev = [...prev, { id: uuid(), value: '' }]). Xoá: setFields(prev = prev.filter(f = f.id !== id)). React Hook Form cung cấp useFieldArray hook xử lý pattern…
Dùng @hookform/resolvers/zod với zodResolver: const { register, handleSubmit } = useForm({ resolver: zodResolver(schema) }). Define schema: const schema = z.object({ email: z.string().email(), age: z.number().min(18) }). Errors type-safe từ Zod được truyền vào formState.errors. Cách tiếp cận hiện đại nhất cho type-safe forms.
React.memo là HOC memoize function component, skip re-render nếu props không thay đổi (shallow comparison). Nên dùng khi component render expensive và props ít thay đổi. Cần kết hợp với useCallback cho function props để có hiệu quả.
React.lazy cho phép lazy load component, chỉ download JavaScript khi component cần render. Suspense hiển thị fallback UI trong khi component đang load. Giúp code splitting, giảm initial bundle size.
Render props là technique chia sẻ code giữa components dùng prop là function trả về React element. Component nhận function prop gọi nó với internal state/logic: <Mouse render={({ x, y }) = <Cat x={x} y={y} /} /. Linh hoạt hơn HOC vì tránh naming collisions.…
Virtualization chỉ render items hiện trong viewport, không render toàn bộ danh sách hàng nghìn items. Thư viện: react-window (lightweight), react-virtualized (feature-rich). Giảm DOM nodes từ hàng nghìn xuống chỉ ~20-50. Cần thiết khi render danh sách lớn (100 items) gây performance issues.
Định nghĩa component bên trong component khác tạo new component type mỗi render, React unmount/remount thay vì update, gây mất state và performance issues. Luôn khai báo components ở top-level module, hoặc ngoài parent component. Nếu cần data từ parent, truyền qua props hoặc dùng…
Thay đổi key của component khiến React unmount component cũ và mount component mới hoàn toàn, reset tất cả state. Kỹ thuật hữu ích để: reset form sau submit <Form key={formKey} /, reset uncontrolled component khi data source thay đổi. Đây là cách React thiết…
useMemo cache kết quả tính toán: useMemo(() = expensiveCalc(data), [data]). useCallback cache function reference: useCallback(() = handleClick(id), [id]). useCallback dùng khi truyền callback xuống child component dùng React.memo. Lạm dụng 2 hooks này có thể giảm performance do overhead.
Cleanup chạy: (1) trước mỗi lần effect re-run khi dependencies thay đổi, (2) khi component unmount. Quan trọng để tránh memory leaks: huỷ subscriptions, abort fetch requests, clear timers. Ví dụ: return () = controller.abort() trong fetch effect.
Custom hooks là functions bắt đầu bằng use, tái sử dụng logic stateful giữa components.
Dùng khi component nhận props ổn định, render tốn kém, và parent re-render thường xuyên mà props không đổi. KHÔNG dùng khi: component luôn nhận props mới (objects/arrays tạo mới), component render nhẹ, hoặc dùng inline callbacks (làm memo mất tác dụng).
<Child style={{color: 'red'}}/ tạo object mới mỗi render → Child luôn re-render dù dùng React.memo. Fix: khai báo object ngoài component hoặc useMemo. Tương tự với onClick={() = handle(id)} → dùng useCallback hoặc data attribute.
Cách tổ chức phổ biến và hiệu quả nhất là theo feature-based, trong đó mỗi tính năng có folder riêng như src/features/auth/ hay src/features/dashboard/, bên trong chứa đầy đủ components, hooks, services, và types của tính năng đó. Phần dùng chung được đặt ở src/components/…
Continuous re-render thường do reference instability hoặc missing dependency — dùng DevTools Profiler để identify. - React DevTools Profiler: tìm component render nhiều. - Nguyên nhân phổ biến: tạo object/array mới trong render, missing dependency array useEffect, context value thay đổi. - Fix: React.memo, useMemo/useCallback,…
Phân biệt bằng ba dấu hiệu: chỉ xảy ra ở môi trường phát triển, mỗi lần mount sinh thêm đúng một lần chạy, và biến mất khi bỏ <StrictMode ra. Nếu thiếu bất kỳ dấu hiệu nào thì nguyên nhân nằm ở chỗ khác. Cách…
(1) Mutate object/array trực tiếp thay vì tạo copy mới (React dùng reference comparison). (2) State nằm trong stale closure. (3) Dùng ref thay vì state. Fix: setState([...arr]) hoặc setState({...obj, key: value}) tạo reference mới. Immer giúp tránh lỗi này.
Phát hiện: Chrome DevTools tab Memory, so sánh các heap snapshot. Nguyên nhân thường gặp: 1. Quên cleanup subscription/timer trong useEffect. 2. Event listener không được remove. 3. Fetch không được huỷ bằng AbortController. Cách sửa: luôn return cleanup function từ useEffect.
Khi trang web load chậm, cần debug có hệ thống từ tổng quan đến chi tiết. Bước đầu tiên mở Chrome DevTools tab Network để xem waterfall — tìm các file quá lớn, requests bị blocking, hoặc API response chậm. Tiếp theo chạy Lighthouse audit…
Với form nhiều fields, giải pháp phù hợp nhất là dùng React Hook Form vì nó hoạt động theo cơ chế uncontrolled components, chỉ re-render field nào thay đổi thay vì toàn bộ form — performance tốt hơn đáng kể so với controlled approach. Kết…
CORS (Cross-Origin Resource Sharing) là cơ chế bảo mật của browser ngăn chặn frontend gọi API từ domain khác nếu server không cho phép — ví dụ app chạy ở localhost:3000 gọi API ở localhost:8080 sẽ bị chặn. Cách fix đúng là backend thêm header…
Hydration mismatch xảy ra khi HTML render từ server khác với kết quả render lần đầu trên client, khiến React báo warning và có thể gây lỗi UI. Nguyên nhân phổ biến nhất là dùng browser-only APIs như window hay localStorage trong quá trình server…
useCallback dùng để giữ nguyên reference của một function qua các lần render, thường dùng khi truyền callback xuống child component đã được bọc React.memo() để tránh re-render không cần thiết. useMemo dùng để cache lại giá trị tính toán nặng, ví dụ như filtered…
React cảnh báo 'missing dependency' khi bạn sử dụng một biến bên trong useEffect nhưng không khai báo nó trong dependency array, vì lúc đó effect sẽ dùng giá trị cũ do stale closure. Nếu không truyền dependency array, effect sẽ chạy sau mỗi lần…
Race condition xảy ra khi nhiều request bất đồng bộ được gửi đi và response trả về không đúng thứ tự, khiến UI hiển thị dữ liệu sai. Cách xử lý phổ biến nhất là dùng AbortController trong cleanup function của useEffect: tạo controller, truyền…
Controlled component là component mà giá trị input được quản lý bởi React state thông qua value và onChange — cho phép kiểm soát hoàn toàn dữ liệu và validate real-time. Uncontrolled component dùng ref để lấy giá trị trực tiếp từ DOM khi cần,…
Prop drilling là tình trạng phải truyền props qua nhiều tầng component trung gian, dù những component đó không thực sự sử dụng props đó — chỉ đóng vai trò chuyển tiếp. Điều này làm code khó bảo trì và khó refactor. Giải pháp phổ…
Key prop giúp React nhận diện phần tử nào đã thay đổi, được thêm mới, hoặc bị xoá trong danh sách để cập nhật DOM hiệu quả thông qua thuật toán reconciliation. Dùng index làm key sẽ gây lỗi nghiêm trọng khi danh sách bị…
Lazy loading là kỹ thuật chỉ tải component khi thực sự cần thiết, giúp giảm kích thước bundle ban đầu và tăng tốc trang. Cách implement: dùng React.lazy() kết hợp Suspense, ví dụ const Dashboard = lazy(() = import('./Dashboard')) rồi bọc trong <Suspense fallback={<Loading /}.…
Virtual scrolling là kỹ thuật chỉ render những item đang hiển thị trong viewport, còn những item ngoài tầm nhìn sẽ bị loại khỏi DOM. Nhờ vậy dù danh sách có hàng chục ngàn item, trình duyệt chỉ cần xử lý vài chục DOM node…
React.memo() là higher-order component bọc quanh functional component để bỏ qua re-render nếu props không thay đổi, so sánh bằng shallow comparison. Nên dùng khi: child component có chi phí render cao, parent re-render thường xuyên mà props của child ít thay đổi. Cần lưu…
Custom Hook là cách trích xuất logic tái sử dụng từ component thành function riêng, tên bắt đầu bằng 'use'. Ví dụ useForm(initialValues) trả về {values, handleChange, reset, errors} để quản lý state và validation của form. Để test custom hook, dùng renderHook từ @testing-library/react-hooks…
Error boundary là component đặc biệt dùng để bắt lỗi JavaScript trong cây component con, ngăn không cho toàn bộ ứng dụng bị crash trắng màn hình. Cách implement: tạo class component với method componentDidCatch và static getDerivedStateFromError, khi có lỗi sẽ hiển thị fallback…
Compound component là pattern trong đó component cha quản lý state nội bộ và chia sẻ cho các component con thông qua Context, tạo nên một nhóm component hoạt động cùng nhau. Ví dụ: <Tabs là cha quản lý tab đang active, <Tab và <TabPanel…
Render prop là pattern component nhận một function làm prop (hoặc children) và gọi function đó với dữ liệu cần thiết để render UI. HOC (Higher-Order Component) là function nhận một component và trả về component mới đã được bổ sung logic. Cả hai đều…
Suspense là cơ chế cho phép React tạm dừng render một component và hiển thị fallback UI (ví dụ loading spinner) cho đến khi component đó sẵn sàng. Hiện tại Suspense hoạt động tốt nhất với React.lazy() cho lazy loading component và với các framework…
Bốn lựa chọn giải quyết các nhu cầu khác nhau, đừng mặc định reach for Redux. - Context API: không phải state manager, chỉ là cơ chế truyền value tránh prop drilling (theme, locale, user đã đăng nhập). Mọi consumer re-render khi value đổi →…
Accessibility không phải tính năng thêm, mà là cách build mặc định. - Semantic HTML trước tiên: dùng <button, <nav, <main, <label thay vì <div onClick. Native element có sẵn role, keyboard handling, focus — ARIA chỉ để vá khi không có element phù hợp.…
Vấn đề thường gặp là khai báo kiểu any cho event hoặc đoán sai kiểu element. Event handlers — React có sẵn type generic theo element và loại event. Lấy đúng kiểu thay vì any: Mẹo: khai báo handler inline trên JSX prop thì TS…
Triết lý RTL: "test như cách người dùng dùng app", không test nội bộ. Thứ tự ưu tiên query (từ tốt nhất xuống): 1. Accessible — getByRole (kèm name), getByLabelText, getByPlaceholderText, getByText. Phản ánh đúng cách user và screen reader nhìn UI. 2. Semantic —…
Error Boundary là class component bắt lỗi trong lúc render của cây con, hiển thị fallback UI thay vì sập cả app (React unmount toàn bộ tree nếu lỗi không được bắt). Nó bắt qua static getDerivedStateFromError (cập nhật state để render fallback) và componentDidCatch…
Custom Hook là một hàm JavaScript thông thường, nhưng tên của nó bắt đầu bằng use và bên trong có thể gọi các Hook khác của React (như useState, useEffect, useContext, v.v.). Tại sao nên sử dụng Custom Hook? - Tái sử dụng Logic: Tách…
Không nên. Cảnh báo đó nói rằng effect đọc một giá trị reactive (prop, state, hoặc biến tính từ chúng) nhưng không khai báo trong dependency array — nghĩa là khi giá trị đó đổi, effect không chạy lại và tiếp tục dùng giá trị…
Quy tắc phân biệt: giá trị nào hiển thị ra UI thì dùng state; giá trị chỉ để ghi nhớ giữa các lần render thì dùng ref. useState useRef --------- Gán giá trị mới gọi setter, gây re-render gán ref.current, không re-render Đọc trong render…
useId tạo một chuỗi id duy nhất và ổn định để nối <label htmlFor với <input id, hoặc để gắn aria-describedby, aria-labelledby. Vì sao không tự sinh: - Math.random() cho giá trị khác nhau giữa server và client → hydration mismatch, React cảnh báo và…
Không cần cờ isMounted. Cảnh báo "Can't perform a React state update on an unmounted component" đã được gỡ khỏi React 18 vì nó báo nhầm nhiều hơn báo đúng: gọi setter trên component đã unmount chỉ là no-op, bản thân nó không rò bộ…
Callback truyền cho setInterval được tạo ở lần render đầu tiên và giữ nguyên mãi. Nó capture biến count của render đó — luôn là 0 — nên mỗi giây đều tính 0 + 1. Ba hướng sửa, chọn theo nhu cầu: 1. Functional updater…
Đây là dấu hiệu effect đang trộn hai loại logic: phần reactive (kết nối lại khi roomId đổi) và phần không reactive (gọi callback mới nhất khi có tin nhắn). Cha tạo onMessage mới mỗi render nên deps đổi liên tục. Các hướng xử lý…
Nguyên tắc: effect dùng để đồng bộ với hệ thống bên ngoài (WebSocket, DOM API, analytics của bên thứ ba theo vòng đời màn hình). Việc xảy ra vì người dùng làm gì đó thuộc về event handler. Mẫu sai hay gặp — gửi đơn…
Vấn đề là cấu trúc state cho phép biểu diễn trạng thái không thể xảy ra. Ba boolean/giá trị độc lập tạo ra tổ hợp mà UI không định nghĩa, và mỗi handler phải nhớ reset đủ cả ba — chỉ cần quên một chỗ…
Một useFetch gọn gàng chỉ giải quyết phần dễ nhất: gọi API rồi set state. Phần khó nằm ở những thứ đến sau khi tính năng lớn lên: - Race condition: hai request cùng key về không đúng thứ tự, kết quả cũ ghi đè…
useEffect chạy sau khi trình duyệt đã vẽ, nên người dùng kịp thấy một khung hình tooltip ở vị trí sai rồi mới nhảy. useLayoutEffect chạy sau khi DOM được cập nhật nhưng trước khi vẽ, nên việc đo và chỉnh vị trí gộp vào…
Cách chặn bằng cờ ref chỉ giấu triệu chứng. Trong StrictMode ở dev, React cố tình mount → unmount → mount lại và chạy setup/cleanup hai lượt để phát hiện effect thiếu cleanup. Bản build production chỉ chạy một lần. Điều nó phơi ra: effect…
Có đúng ba nguồn kích hoạt: 1. State của chính nó thay đổi — gọi setter và giá trị mới khác giá trị cũ (so sánh bằng Object.is). 2. Component cha render lại — mặc định mọi component con render theo, kể cả khi props…
Không. Một lần cập nhật đi qua hai giai đoạn tách biệt: - Render phase — React gọi hàm component, thu được cây element mới, rồi so sánh với cây trước đó. Giai đoạn này chỉ tính toán, không chạm DOM, có thể bị React…
key là định danh của item giữa hai lần render — React dùng nó để quyết định giữ hay huỷ state và DOM node của phần tử. Index chỉ là vị trí, nên khi danh sách thay đổi thứ tự, định danh bị gán nhầm.…
Với DOM element thì gần như không. Tạo một closure là thao tác rất rẻ, và React gắn listener theo cơ chế delegation nên không phải add/remove listener mỗi lần render. Vấn đề chỉ xuất hiện khi hàm đó là prop của một component đã…
memo chỉ bỏ qua render khi mọi prop đều bằng nhau theo so sánh nông (Object.is từng prop). Danh sách kiểm tra theo thứ tự hay gặp: 1. Prop là tham chiếu mới mỗi lần — object/array/function literal, style={{...}}, data={rows.filter(...)}. Đây là nguyên nhân số…
Ý tưởng: element được tạo ở chỗ viết JSX, chứ không phải chỗ render nó. Nếu component nặng do một component ở tầng trên tạo ra (không có state hay thay đổi), thì component đang thay đổi state chỉ nhận nó dưới dạng prop children…
Trước React 18, một lần render là đồng bộ và không thể dừng: React bắt đầu dựng cây component thì phải chạy hết mới trả quyền điều khiển lại cho trình duyệt. Cây lớn thì main thread bị chiếm, gõ phím hoặc click bị trễ.…
Nhấp nháy vì chunk tải rất nhanh (vài chục ms): React kịp hiện fallback rồi thay ngay bằng nội dung thật, mắt người đọc thành một cú giật. Các cách xử lý, theo thứ tự nên thử: 1. Đánh dấu chuyển tab là transition. Trong…
Từ React 19, khi component render một thẻ <title, <meta hoặc <link, React tự động đưa thẻ đó lên <head của trang, kể cả khi component nằm sâu trong cây. Điểm cần biết: - Hoạt động cả khi render phía server (thẻ nằm sẵn trong…
useFormStatus cho một component đọc trạng thái gửi của form cha gần nhất mà không cần truyền prop isSubmitting xuống. Nó đọc trạng thái qua Context do chính thẻ <form cung cấp, mà Context chỉ truyền xuống dưới. Nếu gọi useFormStatus ngay trong ContactForm —…
Cả hai đều hạ ưu tiên phần render nặng để ô input phản hồi ngay. Khác nhau ở chỗ bạn nắm được gì. useTransition — dùng khi bạn tự gọi được setState: useDeferredValue — dùng khi giá trị đến từ props hoặc nơi khác, bạn…
Debounce hoãn theo đồng hồ; useDeferredValue hoãn theo mức bận của máy. debounce 300ms useDeferredValue --------- Điều kiện chạy hết 300ms không gõ thêm React rảnh sau khi xong việc gấp Máy khoẻ vẫn chờ đủ 300ms cập nhật gần như ngay Máy yếu vẫn…
use() đọc giá trị từ một resource — hiện hỗ trợ Promise và Context. Vì sao gọi trong if được: các hook như useState phải gọi cố định thứ tự vì React lưu state trong một danh sách theo vị trí gọi — nhảy cóc…
Waterfall xảy ra khi request sau chỉ bắt đầu sau khi request trước xong, dù chúng độc lập với nhau. Với Suspense, nguyên nhân phổ biến là component con chỉ được render (và mới bắt đầu fetch) sau khi component cha đã có dữ liệu.…
useOptimistic cho bạn một bản sao tạm của state để hiển thị trong lúc một action đang chạy. Rollback là tự động và đó là điểm hay bị hỏi: giá trị optimistic chỉ tồn tại trong phạm vi transition/action đang chạy. Khi action kết thúc…
Chia vai rõ: Suspense bắt trạng thái "chưa có dữ liệu" (promise pending), Error Boundary bắt trạng thái "hỏng" (promise reject hoặc lỗi khi render). Một promise bị reject sẽ đi xuyên qua Suspense và dừng ở Error Boundary gần nhất phía trên. Đặt Error…
Hydration mismatch nghĩa là HTML server sinh ra khác với cây React client dựng ở lần render đầu. React 18 trở đi sẽ vứt HTML của vùng đó và render lại từ client, kèm cảnh báo — kết quả là chậm và có thể nhấp…
Trong Server Component, component là hàm async và await trực tiếp — dữ liệu đã có sẵn khi HTML được sinh ra. Khác biệt chính so với useEffect: - Số round-trip mạng: client cần tải HTML → tải JS → hydrate → mới gọi API…
Nguyên nhân: await ở ngay thân page.tsx chặn toàn bộ output — server không gửi được byte nào cho tới khi truy vấn xong. Cách sửa: đẩy phần chậm xuống component con và bọc <Suspense. Phần nhanh được stream ngay, phần chậm gửi bổ sung…
invalidateQueries đánh dấu cache là stale và refetch các query đang active — dữ liệu chắc chắn đúng vì lấy lại từ server, nhưng tốn thêm một round-trip. setQueryData ghi thẳng kết quả mutation vào cache — không tốn request nào, nhưng bạn phải tự…
Đặt một lớp guard giữa router và page, và luôn nhớ đây chỉ là lớp trải nghiệm — quyền thật do server quyết định. Mô hình thường dùng: session chứa danh sách permissions, mỗi route khai báo quyền nó cần, guard so khớp. Ba điểm…
Nguyên tắc: một nguồn dữ liệu duy nhất cho cả wizard, mỗi bước chỉ hiển thị một phần của nó. State. Giữ toàn bộ giá trị ở form cha (FormProvider của react-hook-form, hoặc một reducer), mỗi bước đọc/ghi qua context. Nếu mỗi bước tự giữ…
Mô hình chuẩn là một mảng object trong form state, mỗi dòng là một phần tử; validate bằng schema mảng để lỗi bám đúng chỉ số dòng. Lỗi thường gặp là dùng key={i}. Khi xoá dòng giữa, React coi các dòng sau là "cùng key,…
Ba lớp cần xử lý: giảm số request, chống kết quả về sai thứ tự, điều khiển bằng bàn phím. Giảm request. Debounce khoảng 250–300ms và bỏ qua chuỗi quá ngắn (< 2 ký tự). Nếu có cache theo từ khoá thì gõ lại từ…
Tối thiểu bốn trạng thái: đang tải, có dữ liệu, rỗng, lỗi. Bỏ sót trạng thái rỗng là lỗi hay gặp nhất — danh sách không có dữ liệu trông y hệt lỗi im lặng. Cách trình bày từng trạng thái: - Loading: dùng skeleton…
Đưa ngôn ngữ hiện tại thành state của ứng dụng chứ không phải biến đọc một lần lúc khởi động. Khi state đổi, React render lại và mọi chuỗi được lấy lại từ từ điển. Những điểm cần nói thêm: - Không hardcode chuỗi trong…