Skip to content

Commit

Permalink
Improve the error message
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Mar 16, 2020
1 parent bed3eb9 commit e81ae47
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 13 deletions.
30 changes: 23 additions & 7 deletions packages/react-reconciler/src/ReactFiberWorkLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -2909,20 +2909,36 @@ if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
}

let didWarnAboutUpdateInRender = false;
let didWarnAboutUpdateInRenderForAnotherComponent;
if (__DEV__) {
didWarnAboutUpdateInRenderForAnotherComponent = new Set();
}

function warnAboutRenderPhaseUpdatesInDEV(fiber) {
if (__DEV__) {
if ((executionContext & RenderContext) !== NoContext) {
switch (fiber.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
console.error(
'Cannot update a component from inside the function body of a ' +
'different component.\n' +
'"%s" updated "%s".',
workInProgress && getComponentName(workInProgress.type),
getComponentName(fiber.type),
);
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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.\n"Cls" updated "Fn".',
'Cannot update a component (`Fn`) from inside the function body of a different component (`Cls`).',
]);
});

Expand Down Expand Up @@ -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.\n"%s" updated "%s".%s',
'Warning: Cannot update a component (`%s`) from inside the function body ' +
'of a different component (`%s`).',
);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ describe('ReactHooksWithNoopRenderer', () => {

function Bar({triggerUpdate}) {
if (triggerUpdate) {
setStep(1);
setStep(x => x + 1);
}
return <Text text="Bar" />;
}
Expand Down Expand Up @@ -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.\n"Bar" updated "Foo".',
'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', () => {
Expand Down

0 comments on commit e81ae47

Please sign in to comment.