Nguyên Nhân Gây Ra Lỗi NàyMở console trình duyệt lên và bạn sẽ thấy thông báo này:
Warning: Cannot update a component from inside the function body of a different component.
React phát hiện rằng trong giai đoạn render của component A, có gì đó đã kích hoạt cập nhật state ở component B. Quá trình render phải thuần túy — không có side effect, không có mutation state. Việc cập nhật state chéo giữa các component bị cấm trong giai đoạn này. Ba pattern sau chiếm phần lớn các trường hợp xảy ra lỗi:
- Một component con gọi hàm setter state của component cha (được truyền qua prop) trực tiếp bên trong phần thân render- Một Redux hoặc Context action được dispatch trong quá trình render- Một custom hook gọi callback đồng bộ trong quá trình render, không nằm trong
useEffect## Tái Hiện LỗiVí dụ tối giản sau sẽ kích hoạt cảnh báo một cách đáng tin cậy:
// Parent.jsx
function Parent() {
const [status, setStatus] = useState('idle');
return (
<div>
<p>Status: {status}</p>
<Child onReady={() => setStatus('ready')} />
</div>
);
}
// Child.jsx — BỊ LỖI
function Child({ onReady }) {
onReady(); // <-- được gọi trong quá trình render, cập nhật state của Parent
return <div>Child content</div>
}
Khi Child render, nó gọi onReady() ngay lập tức. Lời gọi đó tác động đến setStatus bên trong Parent. React bắt được điều này và ném ra cảnh báo.
Cách Sửa Nhanh: Chuyển Lời Gọi Vào useEffectBọc lời gọi trong useEffect — nó chạy sau khi render, không phải trong lúc render:
// Child.jsx — ĐÃ SỬA
import { useEffect } from 'react';
function Child({ onReady }) {
useEffect(() => {
onReady();
}, []); // chạy một lần sau lần render đầu tiên
return <div>Child content</div>
}
Side effect đã được đưa ra khỏi giai đoạn render. onReady được gọi sau khi mount — đúng chỗ nó cần ở.
Chú ý mảng dependency. Nếu onReady được tạo lại sau mỗi lần render của component cha, effect này sẽ chạy trong vòng lặp. Hãy ổn định nó bằng useCallback ở component cha:
// Parent.jsx
const handleReady = useCallback(() => {
setStatus('ready');
}, []);
<Child onReady={handleReady} />
Sửa Lỗi Redux / Context Dispatch Trong Quá Trình RenderDispatch bên trong phần thân component là lỗi tương tự, chỉ khác cú pháp:
// BỊ LỖI
function NotificationBadge({ hasNew }) {
if (hasNew) {
dispatch(markAsSeen()); // <-- trong quá trình render!
}
return <span>{hasNew ? 'New' : ''}</span>
}
// ĐÃ SỬA
function NotificationBadge({ hasNew }) {
useEffect(() => {
if (hasNew) {
dispatch(markAsSeen());
}
}, [hasNew, dispatch]);
return <span>{hasNew ? 'New' : ''}</span>
}
Sửa Lỗi Callback Thông Báo Cho Component Cha Trong Quá Trình RenderMột phiên bản tinh tế hơn: component con gọi prop callback có điều kiện trong quá trình render để thông báo cho component cha về một trạng thái nội bộ nào đó. Trông có vẻ vô hại. Nhưng không phải vậy:
// BỊ LỖI — callback cập nhật state của component cha trong quá trình render của Dropdown
function Dropdown({ isOpen, onStateChange }) {
if (isOpen) {
onStateChange('open'); // kích hoạt cập nhật state ở một component khác
}
return <div>...</div>
}
// ĐÃ SỬA
function Dropdown({ isOpen, onStateChange }) {
useEffect(() => {
onStateChange(isOpen ? 'open' : 'closed');
}, [isOpen, onStateChange]);
return <div>...</div>
}
Đáng lưu ý: việc cập nhật state của chính component trong quá trình render là câu chuyện khác. React cho phép điều này, miễn là bạn dùng điều kiện bảo vệ:
// OK — cập nhật state của chính mình với điều kiện bảo vệ (thay thế hooks cho getDerivedStateFromProps)
function Dropdown({ isOpen }) {
const [open, setOpen] = useState(false);
if (isOpen !== open) {
setOpen(isOpen); // React render lại ngay lập tức, bỏ qua commit trạng thái trung gian
}
return <div>...</div>
}
React render lại component ngay lập tức mà không commit trạng thái trung gian. Chỉ việc cập nhật state của component khác mới bị cấm trong quá trình render.
Sửa Lỗi Custom Hook Gọi Callback Trong Quá Trình RenderCustom hook có thể che giấu vấn đề tương tự. Khi một hook gọi callback bên trong phần thân hook thay vì trong useEffect, bạn sẽ nhận được cùng cảnh báo — chỉ là khó phát hiện hơn:
// Custom hook bị lỗi
function useDataLoader(onLoad) {
const data = fetchFromCache();
if (data) {
onLoad(data); // gọi trong quá trình render!
}
return data;
}
// Custom hook đã sửa
function useDataLoader(onLoad) {
const data = fetchFromCache();
useEffect(() => {
if (data) {
onLoad(data);
}
}, [data, onLoad]);
return data;
}

