Remove propTypes checks for legacy context (#28324)

Part of https://github.com/facebook/react/pull/28207, this is easy to
land in isolation.

The approach I'm taking is slightly different — instead of leaving
validation on for legacy context, I disable the validation (it's
DEV-only) and leave just the parts that drive the runtime logic. I.e.
`contexTypes` and `childContextTypes` *values* are now ignored, the keys
are used just like before.
This commit is contained in:
dan
2024-02-14 16:22:30 +00:00
committed by GitHub
parent f0e808e5bc
commit 64e755d737
3 changed files with 2 additions and 171 deletions
-10
View File
@@ -14,7 +14,6 @@ import {isFiberMounted} from './ReactFiberTreeReflection';
import {disableLegacyContext} from 'shared/ReactFeatureFlags';
import {ClassComponent, HostRoot} from './ReactWorkTags';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
import checkPropTypes from 'shared/checkPropTypes';
import {createCursor, push, pop} from './ReactFiberStack';
@@ -101,11 +100,6 @@ function getMaskedContext(
context[key] = unmaskedContext[key];
}
if (__DEV__) {
const name = getComponentNameFromFiber(workInProgress) || 'Unknown';
checkPropTypes(contextTypes, context, 'context', name);
}
// Cache unmasked context so we can avoid recreating masked context unless necessary.
// Context is created before the class component is instantiated so check for instance.
if (instance) {
@@ -212,10 +206,6 @@ function processChildContext(
);
}
}
if (__DEV__) {
const name = getComponentNameFromFiber(fiber) || 'Unknown';
checkPropTypes(childContextTypes, childContext, 'child context', name);
}
return {...parentContext, ...childContext};
}
@@ -18,7 +18,6 @@
let PropTypes;
let React;
let ReactDOMClient;
let ReactDOMServer;
let ReactTestUtils;
let act;
@@ -29,7 +28,6 @@ describe('ReactContextValidator', () => {
PropTypes = require('prop-types');
React = require('react');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');
act = require('internal-test-utils').act;
});
@@ -159,136 +157,6 @@ describe('ReactContextValidator', () => {
expect(componentDidUpdateContext).toEqual({foo: 'def'});
});
// @gate !disableLegacyContext || !__DEV__
it('should check context types', () => {
class Component extends React.Component {
render() {
return <div />;
}
}
Component.contextTypes = {
foo: PropTypes.string.isRequired,
};
expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toErrorDev(
'Warning: Failed context type: ' +
'The context `foo` is marked as required in `Component`, but its value ' +
'is `undefined`.\n' +
' in Component (at **)',
);
class ComponentInFooStringContext extends React.Component {
getChildContext() {
return {
foo: this.props.fooValue,
};
}
render() {
return <Component />;
}
}
ComponentInFooStringContext.childContextTypes = {
foo: PropTypes.string,
};
// No additional errors expected
ReactTestUtils.renderIntoDocument(
<ComponentInFooStringContext fooValue={'bar'} />,
);
class ComponentInFooNumberContext extends React.Component {
getChildContext() {
return {
foo: this.props.fooValue,
};
}
render() {
return <Component />;
}
}
ComponentInFooNumberContext.childContextTypes = {
foo: PropTypes.number,
};
expect(() =>
ReactTestUtils.renderIntoDocument(
<ComponentInFooNumberContext fooValue={123} />,
),
).toErrorDev(
'Warning: Failed context type: ' +
'Invalid context `foo` of type `number` supplied ' +
'to `Component`, expected `string`.\n' +
' in Component (at **)\n' +
' in ComponentInFooNumberContext (at **)',
);
});
// @gate !disableLegacyContext || !__DEV__
it('should check child context types', () => {
class Component extends React.Component {
getChildContext() {
return this.props.testContext;
}
render() {
return <div />;
}
}
Component.childContextTypes = {
foo: PropTypes.string.isRequired,
bar: PropTypes.number,
};
expect(() =>
ReactTestUtils.renderIntoDocument(<Component testContext={{bar: 123}} />),
).toErrorDev(
'Warning: Failed child context type: ' +
'The child context `foo` is marked as required in `Component`, but its ' +
'value is `undefined`.\n' +
' in Component (at **)',
);
expect(() =>
ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 123}} />),
).toErrorDev(
'Warning: Failed child context type: ' +
'Invalid child context `foo` of type `number` ' +
'supplied to `Component`, expected `string`.\n' +
' in Component (at **)',
);
// No additional errors expected
ReactTestUtils.renderIntoDocument(
<Component testContext={{foo: 'foo', bar: 123}} />,
);
ReactTestUtils.renderIntoDocument(<Component testContext={{foo: 'foo'}} />);
});
it('warns of incorrect prop types on context provider', () => {
const TestContext = React.createContext();
TestContext.Provider.propTypes = {
value: PropTypes.string.isRequired,
};
ReactTestUtils.renderIntoDocument(<TestContext.Provider value="val" />);
class Component extends React.Component {
render() {
return <TestContext.Provider value={undefined} />;
}
}
expect(() => ReactTestUtils.renderIntoDocument(<Component />)).toErrorDev(
'Warning: Failed prop type: The prop `value` is marked as required in ' +
'`Context.Provider`, but its value is `undefined`.\n' +
' in Component (at **)',
);
});
// TODO (bvaughn) Remove this test and the associated behavior in the future.
// It has only been added in Fiber to match the (unintentional) behavior in Stack.
// @gate !disableLegacyContext || !__DEV__
@@ -371,8 +239,6 @@ describe('ReactContextValidator', () => {
'Warning: MiddleMissingContext.childContextTypes is specified but there is no ' +
'getChildContext() method on the instance. You can either define getChildContext() ' +
'on MiddleMissingContext or remove childContextTypes from it.',
'Warning: Failed context type: The context `bar` is marked as required ' +
'in `ChildContextConsumer`, but its value is `undefined`.',
]);
expect(childContext.bar).toBeUndefined();
expect(childContext.foo).toBe('FOO');
@@ -699,24 +565,4 @@ describe('ReactContextValidator', () => {
'Warning: ComponentB: Function components do not support contextType.',
);
});
it('should honor a displayName if set on the context type', () => {
const Context = React.createContext(null);
Context.displayName = 'MyContextType';
function Validator() {
return null;
}
Validator.propTypes = {dontPassToSeeErrorStack: PropTypes.bool.isRequired};
expect(() => {
ReactDOMServer.renderToStaticMarkup(
<Context.Provider>
<Context.Consumer>{() => <Validator />}</Context.Consumer>
</Context.Provider>,
);
}).toErrorDev(
'Warning: Failed prop type: The prop `dontPassToSeeErrorStack` is marked as required in `Validator`, but its value is `undefined`.\n' +
' in Validator (at **)',
);
});
});
@@ -312,7 +312,7 @@ describe('ReactJSXElementValidator', () => {
});
// @gate !disableLegacyContext || !__DEV__
it('should warn on invalid context types', () => {
it('should not warn on invalid context types', () => {
class NullContextTypeComponent extends React.Component {
render() {
return <span>{this.props.prop}</span>;
@@ -321,12 +321,7 @@ describe('ReactJSXElementValidator', () => {
NullContextTypeComponent.contextTypes = {
prop: null,
};
expect(() =>
ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />),
).toErrorDev(
'NullContextTypeComponent: context type `prop` is invalid; it must ' +
'be a function, usually from the `prop-types` package,',
);
ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />);
});
it('should warn if getDefaultProps is specified on the class', () => {