Testing, SSR, hydration và delivery
Test hành vi user và system boundary, render HTML sớm nhưng giữ server/client output deterministic, rồi phát hành bằng artifact có thể quan sát và rollback. Production correctness là một chuỗi, không phải riêng bước build.
1. Test behavior thay vì implementation
Query UI theo role, accessible name, label và visible text; tương tác bằng event gần hành vi user; assert output/status mà user quan sát. Tránh test state nội bộ, Hook call count hoặc CSS selector gắn với markup ngẫu nhiên. Mock network ở protocol boundary để component vẫn chạy data/cache/error logic thật.
| Layer | Nên test | Ví dụ |
|---|---|---|
| Unit | Pure logic và invariant | Reducer transition, formatter, authorization predicate |
| Component integration | User interaction + rendered result | Form validation, dialog focus, optimistic state |
| Route/data | Loader/action/cache/boundary | 404, unauthorized, retry, redirect |
| Browser E2E | Critical cross-system flow | Login, checkout, deep link, reload |
| Accessibility | Keyboard/tree/screen reader | Name-role-state, focus order, live status |
Test async bằng condition observable thay vì sleep cố định. Giữ fixture có ý nghĩa domain, reset server/store giữa tests và chỉ fake timer khi timer là phần contract. Coverage phần trăm không thay mutation/race/error cases.
2. Server rendering và streaming
SSR tạo HTML trên server để browser nhận content sớm; hydration sau đó gắn logic React vào markup. Node.js thường dùng renderToPipeableStream, Web Streams runtime dùng renderToReadableStream. Streaming có thể gửi shell trước rồi reveal Suspense boundary khi data/code sẵn sàng.
const { pipe, abort } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/assets/client.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
},
onShellError(error) {
response.statusCode = 500;
response.end('Unable to render');
},
onError(error) {
reportError(error);
}
});
Headers/status có thể đã gửi khi stream error muộn, nên shell error và recoverable boundary error cần policy riêng. Abort stream khi timeout/client disconnect; escape serialized data để không biến bootstrap payload thành XSS sink.
3. Hydration correctness
hydrateRoot yêu cầu client render ban đầu tạo cùng output với HTML server. Date/time zone, random ID, browser-only branch, data snapshot khác, invalid HTML nesting hoặc extension sửa DOM đều có thể gây mismatch. React coi mismatch là bug; patch behavior không được đảm bảo.
hydrateRoot(document.getElementById('root'), <App initialData={data} />, {
onRecoverableError(error, errorInfo) {
reportError(error, { componentStack: errorInfo.componentStack });
}
});
suppressHydrationWarning. Nó chỉ dành cho chênh lệch không tránh được, phạm vi một level, và không sửa nondeterminism. Ưu tiên serialize snapshot giống nhau, useId cho ID stable và chuyển browser-only work sang Effect hoặc client boundary có chủ đích.4. Server/client component boundaries
Framework quyết định semantics của Server Components, cache, routing và Server Functions. Code chạy server có thể đọc database/secret nhưng dữ liệu truyền sang client phải serializable và không nhạy cảm. Client boundary kéo module graph liên quan vào browser bundle, nên đặt tại vùng cần state, event hoặc browser API thay vì đánh dấu cả route.
- Không import server-only module từ client graph; dùng build guard nếu framework hỗ trợ.
- Không truyền secret hoặc model đầy đủ chỉ vì component cần một field.
- Validate/authenticate/authorize lại mọi Server Function hay action.
- Đo bundle và waterfall sau khi đổi boundary, không dựa vào intuition.
5. Build, cache và security delivery
Phát hành production build với content-hashed assets, immutable cache cho hashed file và revalidation phù hợp cho HTML/data. Đảm bảo deep link rewrite đúng, route chunk tải được, CSP/header security hoạt động và source map không public ngoài chủ đích. Browser compatibility phải khớp targets/polyfill policy.
Deployment cần migration ordering, canary/feature flag khi rủi ro, health check và rollback artifact đã biết tốt. Service worker/CDN cache có thể giữ client cũ nói chuyện với server mới; API và asset strategy cần chịu được version skew.
6. Observability và release verification
Gắn release/version vào error, log và RUM; capture route, browser và correlation context nhưng redact PII/credential. Theo dõi error rate, Web Vitals và critical business success theo percentile/cohort. Alert cần actionable threshold, không chỉ volume thô.
Trước release, smoke test deep link, refresh, back/forward, slow/offline, expired session, API failure, CSP, source maps, accessibility và rollback. Sau release, so baseline theo version và rollback khi user impact vượt guardrail thay vì chờ mọi nguyên nhân được giải thích.