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 nhất là dựng object mới ngay trong JSX của Provider:
// Sai: value là object mới ở mỗi lần App render → mọi consumer render theo
<UserContext.Provider value={{ user, setUser }}>
// Đúng: giữ tham chiếu ổn định
const value = useMemo(() => ({ user, setUser }), [user])
<UserContext.Provider value={value}>Sửa xong chỗ đó vẫn còn vấn đề gốc: mọi consumer vẫn render lại khi user đổi thật, dù component đó chỉ dùng setUser.
Cách chữa là tách context theo nhịp thay đổi:
// Dữ liệu đổi thường xuyên tách khỏi hàm gần như không đổi
<UserStateContext.Provider value={user}>
<UserActionsContext.Provider value={actions}>Component chỉ gọi hành động sẽ đăng ký vào UserActionsContext, và nó không render lại khi user đổi.
Khi cây context lớn và mỗi consumer chỉ cần một phần nhỏ, mẫu selector là bước tiếp theo — use-context-selector, hoặc chuyển hẳn sang một store ngoài như Zustand, vốn hỗ trợ selector sẵn.
Lưu ý: Đừng đưa state đổi mỗi lần gõ phím vào context dùng chung toàn ứng dụng. Context hợp với thứ ít đổi — theme, locale, người dùng đang đăng nhập. Dữ liệu đổi liên tục thì để gần nơi dùng, hoặc để store ngoài lo.