Interview Prep
PracticePortfolio

Categories

  • HTML & CSS41
  • JavaScript40
  • TypeScript42
  • ReactJS40
  • Next.js44
  • Node.js41
  • NestJS44
  • SQL & Database43
  • MongoDB40
  • Git40
  • Docker44
  • GraphQL47
Interview PrepReactJS

ReactJS

40 questions

20 / 40 questions

1

JSX là cú pháp giống HTML được biên dịch thành lời gọi React.createElement(...) (hoặc jsx() runtime mới). Nó chỉ là syntactic sugar — trình duyệt không hiểu JSX.

Một element là object mô tả UI muốn thấy (<Button /> sau khi biên dịch), bất biến và rẻ. Một component là hàm (hoặc class) nhận props và trả về element. Nói ngắn gọn: component là công thức, element là kết quả một lần gọi.

function Button({ label }) { return <button>{label}</button>; }
const el = <Button label="Save" />; // element
2

props là dữ liệu do component cha truyền xuống, read-only với con — con không được sửa props. state là dữ liệu nội bộ do chính component quản lý và có thể thay đổi theo thời gian qua useState/useReducer.

Đổi state sẽ trigger re-render; đổi props (từ cha) cũng khiến con render lại. Quy tắc: dữ liệu đi xuống theo props (one-way data flow), muốn con báo ngược lên cha thì truyền callback qua props.

3

Controlled: giá trị input do React state làm nguồn sự thật — bạn set value và cập nhật qua onChange. React kiểm soát hoàn toàn.

Uncontrolled: DOM tự giữ giá trị, bạn đọc khi cần qua ref (hoặc defaultValue để set giá trị khởi tạo).

// controlled
<input value={name} onChange={e => setName(e.target.value)} />
// uncontrolled
<input defaultValue="" ref={inputRef} />

Controller cho validation/format tức thời; uncontrolled đơn giản hơn cho form ít logic hoặc tích hợp thư viện ngoài.

4

Hai quy tắc: (1) chỉ gọi hook ở top level — không trong vòng lặp, điều kiện, hay hàm lồng; (2) chỉ gọi hook trong React function component hoặc custom hook.

React nhận diện từng hook theo thứ tự gọi giữa các lần render (không có tên). Gọi có điều kiện làm lệch thứ tự → sai state. Plugin eslint-plugin-react-hooks bắt lỗi này tự động.

5

useState trả về [state, setState]. setState không đổi biến ngay lập tức mà lên lịch re-render; giá trị state trong lần render hiện tại là hằng số (snapshot).

Khi state mới phụ thuộc state cũ — hoặc cập nhật nhiều lần liền kề — dùng functional update để tránh dùng giá trị cũ (stale):

setCount(c => c + 1); // an toàn, dựa trên giá trị mới nhất
setCount(count + 1);  // rủi ro nếu count là snapshot cũ
6

useEffect(fn, deps) chạy fn sau khi render được commit ra màn hình. deps quyết định khi nào chạy lại: [] = chỉ sau mount; bỏ trống = mỗi render; [a, b] = khi a hoặc b đổi.

fn có thể trả về hàm cleanup, chạy trước lần effect kế tiếp và khi unmount — dùng để hủy subscription, clear timer, đóng socket.

useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id); // cleanup
}, []);
7

useEffect dành cho đồng bộ với hệ thống ngoài (network, DOM ngoài React, subscription). Nó không dành cho logic phản ứng lại hành động người dùng — cái đó thuộc event handler.

Ví dụ: gửi form khi bấm nút → làm trong onSubmit, không phải effect theo dõi state. Tính giá trị dẫn xuất từ props/state → tính thẳng khi render, không dùng effect + state. Đặt logic sai chỗ gây double-fire, race condition và render thừa.

8

useRef trả về object { current } ổn định qua các lần render và không trigger re-render khi đổi .current.

Hai công dụng: (1) tham chiếu tới DOM node (ref={inputRef} rồi inputRef.current.focus()); (2) lưu giá trị mutable tồn tại giữa các render mà không cần render lại (id của timer, giá trị trước đó, cờ nội bộ).

const renderCount = useRef(0);
renderCount.current++; // không gây render
9

useMemo ghi nhớ một giá trị đã tính; useCallback ghi nhớ danh tính của hàm. Thực chất useCallback(fn, deps) = useMemo(() => fn, deps).

Dùng để giữ tham chiếu ổn định cho React.memo child hoặc dependency của effect — không nên rải khắp nơi vì bản thân memo hóa cũng tốn chi phí và bộ nhớ. Chỉ tối ưu khi có đo đạc thực sự hoặc khi tham chiếu ổn định là bắt buộc về mặt logic.

10

React.memo bọc một component để nó bỏ qua re-render nếu props (so sánh nông) không đổi. Nó chặn re-render lan xuống khi cha render mà props con không đổi.

Chỉ hữu ích khi: component render tốn kém và thường nhận cùng props. Nó vô dụng nếu props chứa object/hàm tạo mới mỗi render (khi đó cần kết hợp useMemo/useCallback ở cha). Lạm dụng làm code phức tạp mà không lợi.

11

useContext(MyContext) đọc giá trị gần nhất từ <MyContext.Provider value={...}>. Dùng để chia sẻ dữ liệu toàn cục (theme, auth, locale) mà không prop drilling.

Pitfall: mọi consumer re-render khi value đổi tham chiếu — nên useMemo cho value. Đặt quá nhiều thứ trong một context gây render thừa; nên tách context theo tần suất thay đổi. Context không phải giải pháp state management thay cho tất cả.

12

Dùng useReducer khi state phức tạp (nhiều field liên quan), khi transition tiếp theo phụ thuộc logic rõ ràng, hoặc khi nhiều event cùng cập nhật một state theo các cách khác nhau.

Reducer gom logic vào một hàm thuần dễ test và tách khỏi UI; dispatch có danh tính ổn định, tiện truyền xuống sâu.

const [state, dispatch] = useReducer(reducer, initialState);
dispatch({ type: 'increment' });
13

Custom hook là hàm có tên bắt đầu bằng use và gọi các hook khác để đóng gói logic tái sử dụng (fetch, form, debounce, subscription). Nó chia sẻ logic chứ không chia sẻ state — mỗi lần gọi có state riêng.

function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn(o => !o), []);
  return [on, toggle];
}

Tách khi cùng một logic stateful lặp lại ở nhiều component, giúp component gọn và dễ test.

14

key giúp React định danh từng phần tử trong list giữa các lần render, để biết item nào được thêm, xóa hay di chuyển thay vì dựng lại toàn bộ. Nhờ đó React giữ state và DOM của các item không đổi.

Key phải ổn định, duy nhất trong cùng list (thường là id từ dữ liệu) và chỉ cần duy nhất giữa các anh em, không cần toàn cục.

15

Khi state đổi, React dựng cây element mới rồi so sánh với cây trước để tính tập thay đổi tối thiểu áp lên DOM thật. Để nhanh, React dùng heuristic: khác type thẻ/component → thay cả subtree; cùng type → giữ node và cập nhật props; với list dùng key để ghép cặp.

Đây là lý do đổi type element hoặc đổi key làm component unmount/remount (mất state), còn cùng type + cùng key thì cập nhật tại chỗ.

16

Khi hai (hay nhiều) component cần chia sẻ cùng một state, ta di chuyển state đó lên component cha chung gần nhất, rồi truyền xuống qua props và truyền callback để con cập nhật.

Cách này giữ một nguồn sự thật duy nhất, tránh state trùng lặp lệch nhau. Ví dụ hai input cùng đồng bộ một giá trị thì state nên nằm ở cha, không phải trong từng input.

17

Prop drilling là việc truyền props qua nhiều tầng component trung gian không dùng đến chúng, chỉ để đưa xuống tầng sâu — gây rối và khó bảo trì.

Cách khắc phục: Context API cho dữ liệu toàn cục; component composition (truyền qua children/slot) để không phải xuyên tầng; hoặc thư viện state management (Redux, Zustand, Jotai) khi state phức tạp và dùng rộng.

18

React.lazy(() => import('./X')) cho phép code-splitting: chỉ tải mã của component khi nó thực sự được render. Suspense bọc quanh component lazy và hiển thị fallback trong lúc chờ tải.

const Chart = lazy(() => import('./Chart'));
<Suspense fallback={<Spinner />}>
  <Chart />
</Suspense>

Giảm bundle ban đầu, cải thiện thời gian tải trang. Suspense cũng dùng cho data fetching trong các framework hỗ trợ.

19

Error boundary là class component cài getDerivedStateFromError và/hoặc componentDidCatch, dùng để bắt lỗi render trong cây con và hiển thị UI dự phòng thay vì làm sập cả app.

Giới hạn: không bắt được lỗi trong event handler, code bất đồng bộ, SSR hay lỗi trong chính error boundary. Hiện chưa có hook tương đương — thường dùng thư viện như react-error-boundary để bọc gọn.

20

createPortal(children, domNode) render phần tử vào một DOM node khác ngoài cây cha, nhưng vẫn giữ nguyên vị trí trong cây React (context, event bubbling vẫn hoạt động theo cây React).

Dùng cho modal, tooltip, dropdown — những UI cần thoát khỏi overflow: hidden/z-index của cha nhưng vẫn nằm trong luồng dữ liệu và sự kiện của component gốc.