Warn about reassigning this.props (#14277)
* Warn about reassigning this.props * Improve the warning * Don't show the spammy bug warning if we suspect it's a component bug
This commit is contained in:
parent
327cf0ee33
commit
a9fdf8a326
|
@ -1743,6 +1743,25 @@ describe('ReactCompositeComponent', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should warn about reassigning this.props while rendering', () => {
|
||||
class Bad extends React.Component {
|
||||
componentDidMount() {}
|
||||
componentDidUpdate() {}
|
||||
render() {
|
||||
this.props = {...this.props};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
expect(() => {
|
||||
ReactDOM.render(<Bad />, container);
|
||||
}).toWarnDev(
|
||||
'It looks like Bad is reassigning its own `this.props` while rendering. ' +
|
||||
'This is not supported and can lead to confusing bugs.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return error if render is not defined', () => {
|
||||
class RenderTestUndefinedRender extends React.Component {}
|
||||
|
||||
|
|
|
@ -130,12 +130,14 @@ let didWarnAboutBadClass;
|
|||
let didWarnAboutContextTypeOnFunctionComponent;
|
||||
let didWarnAboutGetDerivedStateOnFunctionComponent;
|
||||
let didWarnAboutFunctionRefs;
|
||||
export let didWarnAboutReassigningProps;
|
||||
|
||||
if (__DEV__) {
|
||||
didWarnAboutBadClass = {};
|
||||
didWarnAboutContextTypeOnFunctionComponent = {};
|
||||
didWarnAboutGetDerivedStateOnFunctionComponent = {};
|
||||
didWarnAboutFunctionRefs = {};
|
||||
didWarnAboutReassigningProps = false;
|
||||
}
|
||||
|
||||
export function reconcileChildren(
|
||||
|
@ -495,7 +497,7 @@ function updateClassComponent(
|
|||
renderExpirationTime,
|
||||
);
|
||||
}
|
||||
return finishClassComponent(
|
||||
const nextUnitOfWork = finishClassComponent(
|
||||
current,
|
||||
workInProgress,
|
||||
Component,
|
||||
|
@ -503,6 +505,19 @@ function updateClassComponent(
|
|||
hasContext,
|
||||
renderExpirationTime,
|
||||
);
|
||||
if (__DEV__) {
|
||||
let inst = workInProgress.stateNode;
|
||||
if (inst.props !== nextProps) {
|
||||
warning(
|
||||
didWarnAboutReassigningProps,
|
||||
'It looks like %s is reassigning its own `this.props` while rendering. ' +
|
||||
'This is not supported and can lead to confusing bugs.',
|
||||
getComponentName(workInProgress.type) || 'a component',
|
||||
);
|
||||
didWarnAboutReassigningProps = true;
|
||||
}
|
||||
}
|
||||
return nextUnitOfWork;
|
||||
}
|
||||
|
||||
function finishClassComponent(
|
||||
|
|
|
@ -99,6 +99,7 @@ import {
|
|||
UnmountPassive,
|
||||
MountPassive,
|
||||
} from './ReactHookEffectTags';
|
||||
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork';
|
||||
|
||||
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
|
||||
if (__DEV__) {
|
||||
|
@ -234,18 +235,27 @@ function commitBeforeMutationLifeCycles(
|
|||
// but instead we rely on them being set during last render.
|
||||
// TODO: revisit this when we implement resuming.
|
||||
if (__DEV__) {
|
||||
if (finishedWork.type === finishedWork.elementType) {
|
||||
if (
|
||||
finishedWork.type === finishedWork.elementType &&
|
||||
!didWarnAboutReassigningProps
|
||||
) {
|
||||
warning(
|
||||
instance.props === finishedWork.memoizedProps,
|
||||
'Expected instance props to match memoized props before ' +
|
||||
'getSnapshotBeforeUpdate. This is likely due to a bug in React. ' +
|
||||
'Expected %s props to match memoized props before ' +
|
||||
'getSnapshotBeforeUpdate. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
warning(
|
||||
instance.state === finishedWork.memoizedState,
|
||||
'Expected instance state to match memoized state before ' +
|
||||
'getSnapshotBeforeUpdate. This is likely due to a bug in React. ' +
|
||||
'Expected %s state to match memoized state before ' +
|
||||
'getSnapshotBeforeUpdate. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -370,18 +380,27 @@ function commitLifeCycles(
|
|||
// but instead we rely on them being set during last render.
|
||||
// TODO: revisit this when we implement resuming.
|
||||
if (__DEV__) {
|
||||
if (finishedWork.type === finishedWork.elementType) {
|
||||
if (
|
||||
finishedWork.type === finishedWork.elementType &&
|
||||
!didWarnAboutReassigningProps
|
||||
) {
|
||||
warning(
|
||||
instance.props === finishedWork.memoizedProps,
|
||||
'Expected instance props to match memoized props before ' +
|
||||
'componentDidMount. This is likely due to a bug in React. ' +
|
||||
'Expected %s props to match memoized props before ' +
|
||||
'componentDidMount. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
warning(
|
||||
instance.state === finishedWork.memoizedState,
|
||||
'Expected instance state to match memoized state before ' +
|
||||
'componentDidMount. This is likely due to a bug in React. ' +
|
||||
'Expected %s state to match memoized state before ' +
|
||||
'componentDidMount. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -398,18 +417,27 @@ function commitLifeCycles(
|
|||
// but instead we rely on them being set during last render.
|
||||
// TODO: revisit this when we implement resuming.
|
||||
if (__DEV__) {
|
||||
if (finishedWork.type === finishedWork.elementType) {
|
||||
if (
|
||||
finishedWork.type === finishedWork.elementType &&
|
||||
!didWarnAboutReassigningProps
|
||||
) {
|
||||
warning(
|
||||
instance.props === finishedWork.memoizedProps,
|
||||
'Expected instance props to match memoized props before ' +
|
||||
'componentDidUpdate. This is likely due to a bug in React. ' +
|
||||
'Expected %s props to match memoized props before ' +
|
||||
'componentDidUpdate. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
warning(
|
||||
instance.state === finishedWork.memoizedState,
|
||||
'Expected instance state to match memoized state before ' +
|
||||
'componentDidUpdate. This is likely due to a bug in React. ' +
|
||||
'Expected %s state to match memoized state before ' +
|
||||
'componentDidUpdate. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -424,18 +452,27 @@ function commitLifeCycles(
|
|||
const updateQueue = finishedWork.updateQueue;
|
||||
if (updateQueue !== null) {
|
||||
if (__DEV__) {
|
||||
if (finishedWork.type === finishedWork.elementType) {
|
||||
if (
|
||||
finishedWork.type === finishedWork.elementType &&
|
||||
!didWarnAboutReassigningProps
|
||||
) {
|
||||
warning(
|
||||
instance.props === finishedWork.memoizedProps,
|
||||
'Expected instance props to match memoized props before ' +
|
||||
'processing the update queue. This is likely due to a bug in React. ' +
|
||||
'Expected %s props to match memoized props before ' +
|
||||
'processing the update queue. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
warning(
|
||||
instance.state === finishedWork.memoizedState,
|
||||
'Expected instance state to match memoized state before ' +
|
||||
'processing the update queue. This is likely due to a bug in React. ' +
|
||||
'Expected %s state to match memoized state before ' +
|
||||
'processing the update queue. ' +
|
||||
'This might either be because of a bug in React, or because ' +
|
||||
'a component reassigns its own `this.props`. ' +
|
||||
'Please file an issue.',
|
||||
getComponentName(finishedWork.type) || 'instance',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue