Skip to main content

Tips & tricks Inertia.js khi đi làm

Bài này không phải danh sách API. Mục tiêu là trả lời câu hỏi khó hơn: khi nào nên dùng capability nào của Inertia, vì sao, và lỗi production thường xuất hiện ở đâu.
Baseline của các sample: Inertia.js v3 + Laravel + React + TypeScript. Tên API nên luôn đối chiếu lại tài liệu v3 trước khi áp dụng vào project đang dùng version khác.

1. Giữ tư duy server-driven trước khi thêm client architecture

Anti-pattern phổ biến nhất là dùng Inertia nhưng vẫn dựng lại REST SPA đầy đủ:
Với CRUD nội bộ, hãy bắt đầu đơn giản hơn:
Rule: chỉ tách JSON/API khi có consumer thực sự như mobile app, third-party integration, widget độc lập hoặc interaction không nên thay đổi page lifecycle.

2. Server props là snapshot, không phải store bất tử

Một prop nhận từ server mô tả state tại thời điểm page response được tạo. Đừng mirror vô điều kiện:
Sau partial reload/navigation, props.products có thể mới nhưng local copy vẫn cũ. Tốt hơn:
Chỉ tạo local copy khi UI thực sự sở hữu state tạm thời, ví dụ drag reorder chưa commit hoặc optimistic draft.

3. Search/filter: tách draft state và committed URL state

Đây là pattern rất hữu ích:
Tại sao replace: true? Nếu mỗi ký tự tạo một history entry thì Back button trở thành chuỗi t -> tu -> tua -> tuan.

4. Partial reload chỉ nhanh khi server prop cũng lazy

Client:
Server nên tránh evaluate data không cần:
Sai kỳ vọng: only: ['products'] không tự động làm một query đã chạy trước khi Inertia::render() biến mất.

5. Đừng gửi shared props như một global state dump

Tốt:
Không tốt:
Shared props đi cùng rất nhiều response. Payload nhỏ và namespace rõ ràng sẽ giảm coupling đáng kể.

6. Form state: dùng useForm thay vì tự dựng loading/error plumbing

Key CreateProduct giúp form có history identity rõ hơn. Với edit:

7. Nhiều form cùng page: tránh validation collision

Nếu không dùng form helper hoặc có manual visit, hai form có cùng field name dễ đụng errors.
Đừng để component A hiển thị error phát sinh từ component B.

8. Validation Inertia là redirect flow, không phải JSON 422 flow

Đừng viết theo thói quen axios:
Mental model đúng:

9. Loading phải đúng scope

Một progress bar global không thay thế loading của button.
Ví dụ upload:

10. preserveStatepreserveScroll không nên bật theo phản xạ

Search/filter thường cần:
Nhưng navigation sang entity khác thường nên reset local component state. Một rule hữu ích:

11. Scroll container riêng phải khai báo scroll region

Nếu layout dùng container scroll thay vì body:
Nếu quên, Back/Forward có thể restore scroll không như kỳ vọng.

12. Prefetch: tối ưu latency, không phải miễn phí

Prefetch hợp với link có xác suất click cao. Không nên prefetch hàng trăm row chỉ vì API tồn tại. Checklist:

13. Deferred props: tách critical path khỏi secondary data

Server:
Client:
Không defer dữ liệu mà page bắt buộc phải có để quyết định access hoặc render cấu trúc chính.

14. Optimistic update phải có reconciliation plan

Toggle status là candidate tốt:
Tư duy cần đủ ba bước:
Không nên optimistic cho payment, inventory allocation, permission-sensitive action hoặc mutation có business rule dễ reject.

15. Race condition: search request cũ không được thắng request mới

Search debounce giảm request nhưng không loại bỏ mọi race. Khi interaction có thể spam request, hãy tận dụng lifecycle/cancel semantics phù hợp của router thay vì tự tạo nhiều fetch không quản lý. Một pattern UI đơn giản:
Với workflow phức tạp, trace cả request identity trong DevTools thay vì chỉ nhìn kết quả cuối.

16. Dùng useHttp khi request không phải page visit

Inertia v3 có useHttp cho HTTP request độc lập. Ví dụ autocomplete gọi external/internal JSON endpoint không cần thay page:
Decision:
Đừng dùng useHttp để tái tạo API layer cho mọi CRUD nếu redirect + props đã giải quyết tốt.

17. Flash là notification, không phải canonical state

Backend:
Frontend:
Không dùng flash.product như nguồn dữ liệu thay cho prop product/DB.

18. External redirect nên để server phát lệnh rõ ràng

Ví dụ redirect sang cổng thanh toán hoặc external SSO:
Đừng trả một prop URL rồi bắt mọi component tự nhớ gọi window.location nếu redirect là kết quả nghiệp vụ của request server.

19. Instrument router events để debug production

Có thể gắn instrumentation ở bootstrap/layout:
Trong production, log có chọn lọc URL, method, duration và failure class. Tránh log payload nhạy cảm.

20. Dev error modal không phải production error UX

Local development có thể thấy non-Inertia response trong modal rất tiện. Production cần error page Inertia chuẩn:

21. Asset version mismatch là một phần của deployment flow

Sau deploy frontend asset mới, tab cũ có thể đang giữ page version cũ. Inertia có asset versioning để buộc full-page refresh khi version không còn khớp. Practical implication:

22. Authorization: UI hint khác security boundary

Frontend:
Backend vẫn bắt buộc:
Ẩn button chỉ cải thiện UX, không bảo vệ endpoint.

23. Đừng gửi Eloquent model thô nếu page contract quan trọng

Tốt hơn dùng resource/DTO shape rõ:
Điều này giảm accidental prop drift và tránh frontend phụ thuộc cột DB không chủ đích.

24. N+1 vẫn là N+1 dù dùng Inertia

Inertia tối ưu transport/navigation, không sửa query architecture cho bạn. Luôn profile SQL riêng.

25. Một request có nhiều concern: dùng prop boundary để tối ưu

Page dashboard:
Sau khi filter chart:
Thiết kế prop boundary tốt giúp partial reload meaningful; thiết kế một mega prop sẽ làm optimization khó hơn.

26. Checklist trước khi thêm global client store

Hỏi lần lượt:
  1. State này có canonical source ở DB/backend không?
  2. Nó có thể biểu diễn bằng URL không?
  3. Nó chỉ sống trong một form không?
  4. Nó chỉ là UI transient không?
  5. Nó có cần sống qua Back/Forward không?
  6. Inertia props/useForm/useRemember đã giải quyết chưa?
Nếu 1-6 đều không phù hợp, lúc đó mới cân nhắc store riêng.

27. Debugging playbook

Khi một interaction “không chạy đúng”, trace theo lớp:

28. Production checklist ngắn

Trước khi merge một feature Inertia, tự hỏi:

Kết luận

Inertia mạnh nhất khi bạn không cố biến nó thành một SPA architecture khác. Giữ business state ở server, navigation state ở URL, form state trong form helper, local state cho UI thật sự local; sau đó dùng partial reload, deferred, prefetch và optimistic update như các công cụ có semantics rõ ràng thay vì “bật cho nhanh”.

29. Modal form/upload: cân nhắc cancelOnUnmount

Một form nằm trong modal có thể bị unmount trong khi request vẫn đang chạy. Nếu user đóng modal mà bạn không muốn upload/submit tiếp tục, Inertia v3 <Form> hỗ trợ cancelOnUnmount:
Không bật mặc định cho mọi form. Có workflow user đóng panel nhưng vẫn muốn background mutation hoàn tất.

30. Real-time validation: ưu tiên Precognition thay vì duplicate rule ở client

Nếu product SKU cần validate trước submit, Inertia v3 có tích hợp Precognition:
Giá trị chính: rule vẫn ở Laravel; client không phải copy regex/unique rule thành một hệ validation thứ hai.

31. Phân biệt httpExceptionnetworkError

Inertia v3 có event riêng cho non-Inertia HTTP response và lỗi mạng bất ngờ:
Điều này giúp production telemetry phân loại đúng:

32. Prefetch cache có freshness và stale budget riêng

Inertia v3 cho phép cấu hình cacheFor, kể cả fresh/stale duration dạng mảng. Đừng coi prefetch cache như dữ liệu sống mãi.
Hãy document freshness requirement của từng page. Admin catalog có thể chấp nhận stale ngắn; màn permission hoặc inventory nhạy cảm có thể không nên cache cùng policy.

33. Cache invalidation phải đi cùng mutation semantics

Form v3 có option invalidate cache tags. Nếu app dùng prefetch cache/tag strategy, mutation nên nói rõ cache nào không còn đáng tin:
Tư duy quan trọng không phải tên option mà là:

34. withAllErrors() chỉ dùng khi UX thực sự cần nhiều message/field

Mặc định một error đầu tiên mỗi field thường dễ đọc hơn. Nếu product rule cần show toàn bộ lỗi:
Khi đó component phải biết errors.sku có thể là array. Đừng bật chỉ vì “nhiều thông tin hơn” rồi làm UI quá tải.

Tài liệu chính thức

Nội dung thực chiến trong bài được xây dựng dựa trên API và nguyên lý của Inertia.js v3 Documentation. Khi áp dụng vào dự án, hãy đối chiếu API cụ thể với tài liệu chính thức theo phiên bản bạn đang sử dụng.