Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
</summary>
</details>

## 16.8.6 (March 27, 2019)

### React DOM

* Fix an incorrect bailout in `useReducer()`. ([@acdlite](https://github.com/acdlite) in [#15124](https://github.com/facebook/react/pull/15124))
* Fix iframe warnings in Safari DevTools. ([@renanvalentin](https://github.com/renanvalentin) in [#15099](https://github.com/facebook/react/pull/15099))
* Warn if `contextType` is set to `Context.Consumer` instead of `Context`. ([@aweary](https://github.com/aweary) in [#14831](https://github.com/facebook/react/pull/14831))
* Warn if `contextType` is set to invalid values. ([@gaearon](https://github.com/gaearon) in [#15142](https://github.com/facebook/react/pull/15142))

## 16.8.5 (March 22, 2019)

### React DOM
Expand Down
121 changes: 121 additions & 0 deletions packages/react-dom/src/__tests__/ReactServerRendering-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -904,4 +904,125 @@ describe('ReactDOMServer', () => {
' in App (at **)',
]);
});

it('should warn if an invalid contextType is defined', () => {
const Context = React.createContext();

class ComponentA extends React.Component {
// It should warn for both Context.Consumer and Context.Provider
static contextType = Context.Consumer;
render() {
return <div />;
}
}
class ComponentB extends React.Component {
static contextType = Context.Provider;
render() {
return <div />;
}
}

expect(() => {
ReactDOMServer.renderToString(<ComponentA />);
}).toWarnDev(
'Warning: ComponentA defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'Did you accidentally pass the Context.Consumer instead?',
{withoutStack: true},
);

// Warnings should be deduped by component type
ReactDOMServer.renderToString(<ComponentA />);

expect(() => {
ReactDOMServer.renderToString(<ComponentB />);
}).toWarnDev(
'Warning: ComponentB defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'Did you accidentally pass the Context.Provider instead?',
{withoutStack: true},
);
});

it('should not warn when class contextType is null', () => {
class Foo extends React.Component {
static contextType = null; // Handy for conditional declaration
render() {
return this.context.hello.world;
}
}

expect(() => {
ReactDOMServer.renderToString(<Foo />);
}).toThrow("Cannot read property 'world' of undefined");
});

it('should warn when class contextType is undefined', () => {
class Foo extends React.Component {
// This commonly happens with circular deps
// https://github.com/facebook/react/issues/13969
static contextType = undefined;
render() {
return this.context.hello.world;
}
}

expect(() => {
expect(() => {
ReactDOMServer.renderToString(<Foo />);
}).toThrow("Cannot read property 'world' of undefined");
}).toWarnDev(
'Foo defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'However, it is set to undefined. ' +
'This can be caused by a typo or by mixing up named and default imports. ' +
'This can also happen due to a circular dependency, ' +
'so try moving the createContext() call to a separate file.',
{withoutStack: true},
);
});

it('should warn when class contextType is an object', () => {
class Foo extends React.Component {
// Can happen due to a typo
static contextType = {
x: 42,
y: 'hello',
};
render() {
return this.context.hello.world;
}
}

expect(() => {
expect(() => {
ReactDOMServer.renderToString(<Foo />);
}).toThrow("Cannot read property 'hello' of undefined");
}).toWarnDev(
'Foo defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'However, it is set to an object with keys {x, y}.',
{withoutStack: true},
);
});

it('should warn when class contextType is a primitive', () => {
class Foo extends React.Component {
static contextType = 'foo';
render() {
return this.context.hello.world;
}
}

expect(() => {
expect(() => {
ReactDOMServer.renderToString(<Foo />);
}).toThrow("Cannot read property 'world' of undefined");
}).toWarnDev(
'Foo defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'However, it is set to a string.',
{withoutStack: true},
);
});
});
24 changes: 19 additions & 5 deletions packages/react-dom/src/client/ReactInputSelection.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,29 @@ function isInDocument(node) {
);
}

function isSameOriginFrame(iframe) {
try {
// Accessing the contentDocument of a HTMLIframeElement can cause the browser
// to throw, e.g. if it has a cross-origin src attribute.
// Safari will show an error in the console when the access results in "Blocked a frame with origin". e.g:
// iframe.contentDocument.defaultView;
// A safety way is to access one of the cross origin properties: Window or Location
// Which might result in "SecurityError" DOM Exception and it is compatible to Safari.
// https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl

return typeof iframe.contentWindow.location.href === 'string';
} catch (err) {
return false;
}
}

function getActiveElementDeep() {
let win = window;
let element = getActiveElement();
while (element instanceof win.HTMLIFrameElement) {
// Accessing the contentDocument of a HTMLIframeElement can cause the browser
// to throw, e.g. if it has a cross-origin src attribute
try {
win = element.contentDocument.defaultView;
} catch (e) {
if (isSameOriginFrame(element)) {
win = element.contentWindow;
} else {
return element;
}
element = getActiveElement(win.document);
Expand Down
59 changes: 43 additions & 16 deletions packages/react-dom/src/server/ReactPartialRendererContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
import type {ThreadID} from './ReactThreadIDAllocator';
import type {ReactContext} from 'shared/ReactTypes';

import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import getComponentName from 'shared/getComponentName';
import warningWithoutStack from 'shared/warningWithoutStack';
import checkPropTypes from 'prop-types/checkPropTypes';

let ReactDebugCurrentFrame;
let didWarnAboutInvalidateContextType;
if (__DEV__) {
ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
didWarnAboutInvalidateContextType = new Set();
}

const didWarnAboutInvalidateContextType = {};

export const emptyObject = {};
if (__DEV__) {
Object.freeze(emptyObject);
Expand Down Expand Up @@ -75,22 +75,49 @@ export function processContext(
threadID: ThreadID,
) {
const contextType = type.contextType;
if (typeof contextType === 'object' && contextType !== null) {
if (__DEV__) {
if (contextType.$$typeof !== REACT_CONTEXT_TYPE) {
let name = getComponentName(type) || 'Component';
if (!didWarnAboutInvalidateContextType[name]) {
didWarnAboutInvalidateContextType[name] = true;
warningWithoutStack(
false,
'%s defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'Did you accidentally pass the Context.Provider instead?',
name,
);
if (__DEV__) {
if ('contextType' in (type: any)) {
let isValid =
// Allow null for conditional declaration
contextType === null ||
(contextType !== undefined &&
contextType.$$typeof === REACT_CONTEXT_TYPE &&
contextType._context === undefined); // Not a <Context.Consumer>

if (!isValid && !didWarnAboutInvalidateContextType.has(type)) {
didWarnAboutInvalidateContextType.add(type);

let addendum = '';
if (contextType === undefined) {
addendum =
' However, it is set to undefined. ' +
'This can be caused by a typo or by mixing up named and default imports. ' +
'This can also happen due to a circular dependency, so ' +
'try moving the createContext() call to a separate file.';
} else if (typeof contextType !== 'object') {
addendum = ' However, it is set to a ' + typeof contextType + '.';
} else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
addendum = ' Did you accidentally pass the Context.Provider instead?';
} else if (contextType._context !== undefined) {
// <Context.Consumer>
addendum = ' Did you accidentally pass the Context.Consumer instead?';
} else {
addendum =
' However, it is set to an object with keys {' +
Object.keys(contextType).join(', ') +
'}.';
}
warningWithoutStack(
false,
'%s defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext().%s',
getComponentName(type) || 'Component',
addendum,
);
}
}
}
if (typeof contextType === 'object' && contextType !== null) {
validateContextBounds(contextType, threadID);
return contextType[threadID];
} else {
Expand Down
46 changes: 37 additions & 9 deletions packages/react-reconciler/src/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import shallowEqual from 'shared/shallowEqual';
import getComponentName from 'shared/getComponentName';
import invariant from 'shared/invariant';
import warningWithoutStack from 'shared/warningWithoutStack';
import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
import {REACT_CONTEXT_TYPE, REACT_PROVIDER_TYPE} from 'shared/ReactSymbols';

import {startPhaseTimer, stopPhaseTimer} from './ReactDebugFiberPerf';
import {resolveDefaultProps} from './ReactFiberLazyComponent';
Expand Down Expand Up @@ -513,23 +513,51 @@ function constructClassInstance(
let unmaskedContext = emptyContextObject;
let context = null;
const contextType = ctor.contextType;
if (typeof contextType === 'object' && contextType !== null) {
if (__DEV__) {
if (
contextType.$$typeof !== REACT_CONTEXT_TYPE &&
!didWarnAboutInvalidateContextType.has(ctor)
) {

if (__DEV__) {
if ('contextType' in ctor) {
let isValid =
// Allow null for conditional declaration
contextType === null ||
(contextType !== undefined &&
contextType.$$typeof === REACT_CONTEXT_TYPE &&
contextType._context === undefined); // Not a <Context.Consumer>

if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
didWarnAboutInvalidateContextType.add(ctor);

let addendum = '';
if (contextType === undefined) {
addendum =
' However, it is set to undefined. ' +
'This can be caused by a typo or by mixing up named and default imports. ' +
'This can also happen due to a circular dependency, so ' +
'try moving the createContext() call to a separate file.';
} else if (typeof contextType !== 'object') {
addendum = ' However, it is set to a ' + typeof contextType + '.';
} else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
addendum = ' Did you accidentally pass the Context.Provider instead?';
} else if (contextType._context !== undefined) {
// <Context.Consumer>
addendum = ' Did you accidentally pass the Context.Consumer instead?';
} else {
addendum =
' However, it is set to an object with keys {' +
Object.keys(contextType).join(', ') +
'}.';
}
warningWithoutStack(
false,
'%s defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'Did you accidentally pass the Context.Provider instead?',
'contextType should point to the Context object returned by React.createContext().%s',
getComponentName(ctor) || 'Component',
addendum,
);
}
}
}

if (typeof contextType === 'object' && contextType !== null) {
context = readContext((contextType: any));
} else {
unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
Expand Down
Loading