mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
improve error message for cross-functional component updates (#18316)
* improve error message for cross-functional component updates * correctly use %s by quoting it * use workInProgress and lint * add test assertion * fix test * Improve the error message Co-authored-by: Dan Abramov <dan.abramov@me.com>
This commit is contained in:
co-authored by
Dan Abramov
parent
c0ed0a2468
commit
95df39b6b1
+23
-4
@@ -2919,6 +2919,11 @@ if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
|
||||
}
|
||||
|
||||
let didWarnAboutUpdateInRender = false;
|
||||
let didWarnAboutUpdateInRenderForAnotherComponent;
|
||||
if (__DEV__) {
|
||||
didWarnAboutUpdateInRenderForAnotherComponent = new Set();
|
||||
}
|
||||
|
||||
function warnAboutRenderPhaseUpdatesInDEV(fiber) {
|
||||
if (__DEV__) {
|
||||
if ((executionContext & RenderContext) !== NoContext) {
|
||||
@@ -2926,10 +2931,24 @@ function warnAboutRenderPhaseUpdatesInDEV(fiber) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case SimpleMemoComponent: {
|
||||
console.error(
|
||||
'Cannot update a component from inside the function body of a ' +
|
||||
'different component.',
|
||||
);
|
||||
const renderingComponentName =
|
||||
(workInProgress && getComponentName(workInProgress.type)) ||
|
||||
'Unknown';
|
||||
const setStateComponentName =
|
||||
getComponentName(fiber.type) || 'Unknown';
|
||||
const dedupeKey =
|
||||
renderingComponentName + ' ' + setStateComponentName;
|
||||
if (!didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) {
|
||||
didWarnAboutUpdateInRenderForAnotherComponent.add(dedupeKey);
|
||||
console.error(
|
||||
'Cannot update a component (`%s`) from inside the function body of a ' +
|
||||
'different component (`%s`). To locate the bad setState() call inside `%s`, ' +
|
||||
'follow the stack trace as described in https://fb.me/setstate-in-render',
|
||||
setStateComponentName,
|
||||
renderingComponentName,
|
||||
renderingComponentName,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ClassComponent: {
|
||||
|
||||
@@ -1087,7 +1087,7 @@ describe('ReactHooks', () => {
|
||||
),
|
||||
).toErrorDev([
|
||||
'Context can only be read while React is rendering',
|
||||
'Cannot update a component from inside the function body of a different component.',
|
||||
'Cannot update a component (`Fn`) from inside the function body of a different component (`Cls`).',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1783,8 +1783,8 @@ describe('ReactHooks', () => {
|
||||
if (__DEV__) {
|
||||
expect(console.error).toHaveBeenCalledTimes(2);
|
||||
expect(console.error.calls.argsFor(0)[0]).toContain(
|
||||
'Warning: Cannot update a component from inside the function body ' +
|
||||
'of a different component.%s',
|
||||
'Warning: Cannot update a component (`%s`) from inside the function body ' +
|
||||
'of a different component (`%s`).',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
+14
-3
@@ -412,7 +412,7 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
|
||||
function Bar({triggerUpdate}) {
|
||||
if (triggerUpdate) {
|
||||
setStep(1);
|
||||
setStep(x => x + 1);
|
||||
}
|
||||
return <Text text="Bar" />;
|
||||
}
|
||||
@@ -440,10 +440,21 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
expect(() =>
|
||||
expect(Scheduler).toFlushAndYield(['Foo [0]', 'Bar', 'Foo [1]']),
|
||||
).toErrorDev([
|
||||
'Cannot update a component from inside the function body of a ' +
|
||||
'different component.',
|
||||
'Cannot update a component (`Foo`) from inside the function body of a ' +
|
||||
'different component (`Bar`). To locate the bad setState() call inside `Bar`',
|
||||
]);
|
||||
});
|
||||
|
||||
// It should not warn again (deduplication).
|
||||
await ReactNoop.act(async () => {
|
||||
root.render(
|
||||
<>
|
||||
<Foo />
|
||||
<Bar triggerUpdate={true} />
|
||||
</>,
|
||||
);
|
||||
expect(Scheduler).toFlushAndYield(['Foo [1]', 'Bar', 'Foo [2]']);
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps restarting until there are no more new updates', () => {
|
||||
|
||||
Reference in New Issue
Block a user