Việc làm

23/09 · Hồ Chí Minh · Hybrid

Fullstack Engineer (ReactJS, Ruby on Rails)

Arches · Fullstack Developer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

10 câu hỏi có thể gặp khi phỏng vấn

Chọn theo kỹ năng và cấp của tin (ReactJS, JavaScript & TypeScript, SQL & Database). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.

  1. 01useEffect dùng để làm gì? Dependency array và hàm cleanup hoạt động ra sao?ReactJS · Junior

    Gợi ý trả lời

    useEffect dùng để đồng bộ component với hệ thống bên ngoài: subscribe sự kiện, kết nối WebSocket, gọi thư viện không phải React, gọi API.

    • Không có dependency array: chạy sau mỗi lần render.
    • []: chạy sau lần mount đầu tiên.
    • [a, b]: chạy lại khi a hoặc b đổi.
    • Hàm return là cleanup, chạy trước lần effect kế tiếp và khi unmount.
    useEffect(() => {
      const id = setInterval(tick, 1000);
      return () => clearInterval(id);
    }, []);
    

    Ở development, Strict Mode cố ý chạy effect hai lần để bạn phát hiện thiếu cleanup.

    Hay sai: dùng effect để tính dữ liệu dẫn xuất từ state/props (setFullName(first + last)). Hãy tính thẳng trong lúc render; effect thừa gây render hai lần và dễ lệch dữ liệu.

    Ôn thêm câu hỏi ReactJS →
  2. 02Controlled component và uncontrolled component khác nhau thế nào? Khi nào dùng loại nào?ReactJS · Junior

    Gợi ý trả lời

    Controlled: giá trị input nằm trong state React, mọi thay đổi đi qua onChange. Uncontrolled: DOM tự giữ giá trị, bạn đọc khi cần qua ref hoặc khi form submit.

    <input value={name} onChange={e => setName(e.target.value)} />
    <input defaultValue="An" name="name" />
    
    • Controlled hợp khi cần validate ngay khi gõ, định dạng giá trị, hoặc ô này phụ thuộc ô kia.
    • Uncontrolled đơn giản hơn, ít render hơn; hợp với form lớn hoặc khi dùng thư viện như React Hook Form. React 19 còn hỗ trợ truyền hàm vào <form action> và đọc dữ liệu bằng FormData.

    Hay sai: chuyển một input từ value={undefined} sang có giá trị, khiến React cảnh báo đổi từ uncontrolled sang controlled. Khởi tạo state bằng "" thay vì undefined.

    Ôn thêm câu hỏi ReactJS →
  3. 03Khi nào nên dùng useMemo, useCallback và React.memo? Dùng tràn lan có sao không?ReactJS

    Gợi ý trả lời

    Cả ba dùng để tránh tính toán hoặc render lại không cần thiết:

    • useMemo: nhớ kết quả của phép tính tốn kém.
    • useCallback: nhớ hàm, để tham chiếu không đổi giữa các lần render.
    • React.memo: bỏ qua render component con nếu props không đổi (so sánh nông).

    Chúng thường phải đi cùng nhau: truyền hàm mới mỗi lần render vào component đã memo thì memo vô tác dụng.

    Dùng tràn lan làm code khó đọc, tốn công so sánh dependency, và dễ sai khi thiếu dependency. Chỉ nên dùng khi đã đo bằng React DevTools Profiler và thấy chậm, hoặc khi giá trị đó là dependency của effect.

    React Compiler có thể tự thêm memo khi build, giảm nhu cầu viết tay.

    Người phỏng vấn muốn nghe: trước khi memo, hãy thử đưa state xuống thấp hơn hoặc truyền component qua children để giảm phạm vi render lại.

    Ôn thêm câu hỏi ReactJS →
  4. 04this trong JavaScript được xác định thế nào? Arrow function khác function thường ở điểm nào?JavaScript & TypeScript · Junior

    Gợi ý trả lời

    Với function thường, this phụ thuộc vào cách gọi, không phải nơi định nghĩa:

    • obj.method(): this là obj.
    • Gọi hàm trơn fn(): undefined trong strict mode (module và class luôn là strict).
    • new Fn(): object mới được tạo.
    • fn.call(x), fn.apply(x), fn.bind(x): this là x.

    Arrow function không có this riêng; nó lấy this của phạm vi bao ngoài. Arrow function cũng không có arguments và không dùng được với new.

    const btn = { label: "OK", click() { setTimeout(() => console.log(this.label)); } };
    

    Hay sai: truyền obj.method làm callback (setTimeout(obj.method)) rồi mất this. Cách sửa: bind, hoặc bọc trong arrow function.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  5. 05Event loop của JavaScript hoạt động thế nào? Đoạn code sau in ra theo thứ tự nào?JavaScript & TypeScript

    Gợi ý trả lời

    JavaScript chạy trên một thread. Code đồng bộ chạy hết trên call stack trước, sau đó event loop lấy việc từ hàng đợi. Microtask (Promise .then, await, queueMicrotask) luôn được chạy hết trước khi sang macrotask tiếp theo (setTimeout, sự kiện I/O, sự kiện người dùng).

    console.log(1);
    setTimeout(() => console.log(2));
    Promise.resolve().then(() => console.log(3));
    console.log(4);
    // 1, 4, 3, 2
    
    • Trên trình duyệt, việc render thường diễn ra giữa các macrotask.
    • Một vòng lặp đồng bộ dài, hoặc chuỗi microtask không dứt, sẽ làm treo giao diện.

    Người phỏng vấn muốn nghe: setTimeout(fn, 0) không chạy ngay mà chỉ được xếp hàng, và vì sao microtask được ưu tiên.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  6. 06Composite index (index nhiều cột) hoạt động ra sao? Thứ tự cột có quan trọng không?SQL & Database

    Gợi ý trả lời

    Có. Composite index sắp xếp theo cột đầu, rồi đến cột thứ hai trong cùng giá trị cột đầu, v.v. Vì vậy nó chỉ dùng hiệu quả khi query dùng các cột từ trái sang (leftmost prefix).

    Với INDEX (status, created_at):

    • WHERE status = 'paid' AND created_at > ...: dùng tốt.
    • WHERE status = 'paid' ORDER BY created_at DESC: dùng cho cả lọc lẫn sắp xếp.
    • WHERE created_at > ... (không có status): thường không dùng được.

    Nguyên tắc chọn thứ tự: cột so sánh bằng (=) đặt trước, cột so sánh khoảng hoặc sắp xếp đặt sau. Nếu index chứa đủ mọi cột query cần (covering index) thì DB không phải đọc lại bảng.

    Hay sai: tạo riêng index cho từng cột rồi nghĩ rằng chúng sẽ kết hợp hiệu quả như một composite index.

    Ôn thêm câu hỏi SQL & Database →
  7. 07Custom hook là gì? Các quy tắc của hooks là gì và vì sao có chúng?ReactJS

    Gợi ý trả lời

    Custom hook là hàm bắt đầu bằng use, dùng các hook khác bên trong, để tái sử dụng logic có state giữa nhiều component (không chia sẻ state, mỗi component có bản riêng).

    function useDebounce(value, ms) {
      const [debounced, setDebounced] = useState(value);
      useEffect(() => {
        const id = setTimeout(() => setDebounced(value), ms);
        return () => clearTimeout(id);
      }, [value, ms]);
      return debounced;
    }
    

    Quy tắc: chỉ gọi hook ở cấp cao nhất của component hoặc custom hook; không gọi trong if, vòng lặp hay sau return sớm. Lý do: React nhận diện từng hook theo thứ tự gọi, đổi thứ tự là state bị ghép nhầm.

    Ngoại lệ: hook use() của React 19 được gọi trong điều kiện.

    Người phỏng vấn muốn nghe: bạn bật ESLint plugin react-hooks để bắt các lỗi này tự động.

    Ôn thêm câu hỏi ReactJS →
  8. 08Bạn chọn cách quản lý state thế nào: Context, Redux Toolkit, Zustand hay TanStack Query?ReactJS

    Gợi ý trả lời

    Trước hết phân loại state:

    • Server state (dữ liệu từ API): dùng TanStack Query hoặc SWR. Chúng lo cache, loading, refetch, đồng bộ. Phần lớn "global state" trong app thực ra thuộc loại này.
    • UI state cục bộ: useState/useReducer ngay trong component.
    • State trên URL (bộ lọc, trang): để trên query string, chia sẻ link được.
    • Client state toàn cục thật sự (giỏ hàng, theme, user đang đăng nhập): Context nếu ít thay đổi; Zustand hoặc Redux Toolkit nếu thay đổi thường xuyên hoặc logic phức tạp.

    Redux Toolkit hợp khi team lớn cần quy ước chặt và DevTools mạnh. Zustand gọn nhẹ hơn.

    Hay sai: gom state hay thay đổi vào một Context lớn. Mọi component dùng Context đó render lại mỗi lần bất kỳ giá trị nào đổi. Hãy tách Context, hoặc dùng thư viện có selector.

    Ôn thêm câu hỏi ReactJS →
  9. 09Stale closure trong React là gì? Vì sao setInterval trong useEffect cứ in ra giá trị cũ?ReactJS

    Gợi ý trả lời

    Mỗi lần render tạo ra hàm mới, "chụp" lại props và state của lần render đó. Nếu effect chỉ chạy một lần, callback bên trong mãi thấy giá trị lúc mount.

    useEffect(() => {
      const id = setInterval(() => {
        setCount(count + 1); // count luôn là 0
      }, 1000);
      return () => clearInterval(id);
    }, []);
    

    Cách sửa:

    • Dùng functional update: setCount(c => c + 1), không cần đọc count.
    • Khai báo đúng dependency (effect chạy lại khi giá trị đổi, nhớ cleanup).
    • Với giá trị chỉ cần đọc mới nhất mà không muốn chạy lại effect: lưu vào useRef.

    Hay sai: tắt cảnh báo exhaustive-deps của ESLint cho xong. Cảnh báo đó thường chính là dấu hiệu stale closure.

    Ôn thêm câu hỏi ReactJS →
  10. 10Bạn hãy giới thiệu ngắn về bản thân.Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Đây là câu mở đầu gần như chắc chắn có. Dùng công thức Hiện tại – Quá khứ – Tương lai, gói trong khoảng 1–2 phút.

    • Hiện tại: vị trí, số năm kinh nghiệm, mảng/công nghệ chính.
    • Quá khứ: một hai dự án hoặc thành tích liên quan nhất tới vị trí đang tuyển.
    • Tương lai: vì sao bạn quan tâm tới vị trí này.

    Câu mẫu: "Em là backend developer, khoảng 3 năm kinh nghiệm, chủ yếu làm Java Spring cho hệ thống thanh toán. Ở dự án gần nhất em phụ trách module đối soát và tối ưu các truy vấn báo cáo chạy chậm. Em đang tìm môi trường làm sản phẩm lâu dài để đi sâu hơn về thiết kế hệ thống, nên rất quan tâm tới vị trí này."

    Hay sai: kể từ quê quán, trường cấp ba, hoặc đọc lại nguyên CV.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở Arches?

Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.

Chia sẻ kinh nghiệm phỏng vấn