Compare commits

...

7 Commits

Author SHA1 Message Date
Dan Abramov
487f4bf2ee Update versions for 16.8.6 2019-03-27 23:55:48 -07:00
Dan Abramov
297165f1e1 Port tests to old API 2019-03-27 15:40:51 -07:00
Dan Abramov
cd5b8950a0 Changelog 2019-03-27 08:54:25 -07:00
Renan Valentin
f00be84b81 fix(react-dom): access iframe contentWindow instead of contentDocument (#15099)
MDN has a list of methods for obtaining the window reference of an
iframe:

https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Syntax

fix(react-dom): check if iframe belongs to the same origin

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:

```javascript
try {
 $0.contentDocument.defaultView
} catch (err) {
  console.log('err', err)
}

> Blocked a frame with origin X from accessing a frame with origin Y. Protocols, domains, and ports must match.
> err – TypeError: null is not an object (evaluating '$0.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.

```javascript
try {
 $0.contentWindow.location.href
} catch (err) {
 console.log('err', err)
}

> err – SecurityError: Blocked a frame with origin "http://localhost:3001" from accessing a cross-origin frame. Protocols, domains, and ports must match.
```

https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl
2019-03-27 08:45:32 -07:00
Dan Abramov
e0c2c56dfd Improve warning for invalid class contextType (#15142)
* Improve warning for invalid class contextType

* Don't warn for null

* Grammar
2019-03-27 08:45:20 -07:00
Brandon Dail
aa8736a3a9 Warn for Context.Consumer with contextType (#14831) 2019-03-27 08:45:13 -07:00
Andrew Clark
d9fa20d52f Eager bailout optimization should always compare to latest reducer (#15124)
* Eager bailout optimization should always compare to latest reducer

* queue.eagerReducer -> queue.lastRenderedReducer

This name is a bit more descriptive.

* Add test case that uses preceding render phase update
2019-03-27 08:45:04 -07:00
18 changed files with 430 additions and 64 deletions

View File

@@ -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

View File

@@ -1,7 +1,7 @@
{
"name": "create-subscription",
"description": "utility for subscribing to external data sources inside React components",
"version": "16.8.5",
"version": "16.8.6",
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git",

View File

@@ -1,6 +1,6 @@
{
"name": "jest-react",
"version": "0.6.5",
"version": "0.6.6",
"description": "Jest matchers and utilities for testing React components.",
"main": "index.js",
"repository": {

View File

@@ -1,7 +1,7 @@
{
"name": "react-art",
"description": "React ART is a JavaScript library for drawing vector graphics using React. It provides declarative and reactive bindings to the ART library. Using the same declarative API you can render the output to either Canvas, SVG or VML (IE8).",
"version": "16.8.5",
"version": "16.8.6",
"main": "index.js",
"repository": {
"type": "git",
@@ -27,7 +27,7 @@
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.5"
"scheduler": "^0.13.6"
},
"peerDependencies": {
"react": "^16.0.0"

View File

@@ -1,6 +1,6 @@
{
"name": "react-dom",
"version": "16.8.5",
"version": "16.8.6",
"description": "React package for working with the DOM.",
"main": "index.js",
"repository": {
@@ -20,7 +20,7 @@
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.5"
"scheduler": "^0.13.6"
},
"peerDependencies": {
"react": "^16.0.0"

View File

@@ -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},
);
});
});

View File

@@ -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);

View File

@@ -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);
@@ -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 {

View File

@@ -1,6 +1,6 @@
{
"name": "react-is",
"version": "16.8.5",
"version": "16.8.6",
"description": "Brand checking of React Elements.",
"main": "index.js",
"repository": {

View File

@@ -1,7 +1,7 @@
{
"name": "react-reconciler",
"description": "React package for creating custom renderers.",
"version": "0.20.3",
"version": "0.20.4",
"keywords": [
"react"
],
@@ -33,7 +33,7 @@
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.5"
"scheduler": "^0.13.6"
},
"browserify": {
"transform": [

View File

@@ -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';
@@ -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);

View File

@@ -89,8 +89,8 @@ type Update<S, A> = {
type UpdateQueue<S, A> = {
last: Update<S, A> | null,
dispatch: (A => mixed) | null,
eagerReducer: ((S, A) => S) | null,
eagerState: S | null,
lastRenderedReducer: ((S, A) => S) | null,
lastRenderedState: S | null,
};
export type HookType =
@@ -591,8 +591,8 @@ function mountReducer<S, I, A>(
const queue = (hook.queue = {
last: null,
dispatch: null,
eagerReducer: reducer,
eagerState: (initialState: any),
lastRenderedReducer: reducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<A> = (queue.dispatch = (dispatchAction.bind(
null,
@@ -615,6 +615,8 @@ function updateReducer<S, I, A>(
'Should have a queue. This is likely a bug in React. Please file an issue.',
);
queue.lastRenderedReducer = reducer;
if (numberOfReRenders > 0) {
// This is a re-render. Apply the new render phase updates to the previous
// work-in-progress hook.
@@ -650,8 +652,7 @@ function updateReducer<S, I, A>(
hook.baseState = newState;
}
queue.eagerReducer = reducer;
queue.eagerState = newState;
queue.lastRenderedState = newState;
return [newState, dispatch];
}
@@ -730,8 +731,7 @@ function updateReducer<S, I, A>(
hook.baseUpdate = newBaseUpdate;
hook.baseState = newBaseState;
queue.eagerReducer = reducer;
queue.eagerState = newState;
queue.lastRenderedState = newState;
}
const dispatch: Dispatch<A> = (queue.dispatch: any);
@@ -749,8 +749,8 @@ function mountState<S>(
const queue = (hook.queue = {
last: null,
dispatch: null,
eagerReducer: basicStateReducer,
eagerState: (initialState: any),
lastRenderedReducer: basicStateReducer,
lastRenderedState: (initialState: any),
});
const dispatch: Dispatch<
BasicStateAction<S>,
@@ -1129,21 +1129,21 @@ function dispatchAction<S, A>(
// The queue is currently empty, which means we can eagerly compute the
// next state before entering the render phase. If the new state is the
// same as the current state, we may be able to bail out entirely.
const eagerReducer = queue.eagerReducer;
if (eagerReducer !== null) {
const lastRenderedReducer = queue.lastRenderedReducer;
if (lastRenderedReducer !== null) {
let prevDispatcher;
if (__DEV__) {
prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
}
try {
const currentState: S = (queue.eagerState: any);
const eagerState = eagerReducer(currentState, action);
const currentState: S = (queue.lastRenderedState: any);
const eagerState = lastRenderedReducer(currentState, action);
// Stash the eagerly computed state, and the reducer used to compute
// it, on the update object. If the reducer hasn't changed by the
// time we enter the render phase, then the eager state can be used
// without calling the reducer again.
update.eagerReducer = eagerReducer;
update.eagerReducer = lastRenderedReducer;
update.eagerState = eagerState;
if (is(eagerState, currentState)) {
// Fast path. We can bail out without scheduling React to re-render.

View File

@@ -1871,4 +1871,89 @@ describe('ReactHooksWithNoopRenderer', () => {
// );
});
});
it('eager bailout optimization should always compare to latest rendered reducer', () => {
// Edge case based on a bug report
let setCounter;
function App() {
const [counter, _setCounter] = useState(1);
setCounter = _setCounter;
return <Component count={counter} />;
}
function Component({count}) {
const [state, dispatch] = useReducer(() => {
// This reducer closes over a value from props. If the reducer is not
// properly updated, the eager reducer will compare to an old value
// and bail out incorrectly.
ReactNoop.yield('Reducer: ' + count);
return count;
}, -1);
useEffect(
() => {
ReactNoop.yield('Effect: ' + count);
dispatch();
},
[count],
);
ReactNoop.yield('Render: ' + state);
return <span prop={count} />;
}
ReactNoop.render(<App />);
expect(ReactNoop.flush()).toEqual(['Render: -1']);
ReactNoop.flushPassiveEffects();
expect(ReactNoop.flush()).toEqual([
'Effect: 1',
'Reducer: 1',
'Reducer: 1',
'Render: 1',
]);
expect(ReactNoop.getChildren()).toEqual([span(1)]);
act(() => {
setCounter(2);
});
expect(ReactNoop.flush()).toEqual([
'Render: 1',
'Effect: 2',
'Reducer: 2',
'Reducer: 2',
'Render: 2',
]);
expect(ReactNoop.getChildren()).toEqual([span(2)]);
});
it('should update latest rendered reducer when a preceding state receives a render phase update', () => {
// Similar to previous test, except using a preceding render phase update
// instead of new props.
let dispatch;
function App() {
const [step, setStep] = useState(0);
const [shadow, _dispatch] = useReducer(() => step, step);
dispatch = _dispatch;
if (step < 5) {
setStep(step + 1);
}
ReactNoop.yield(`Step: ${step}, Shadow: ${shadow}`);
return <span prop={shadow} />;
}
ReactNoop.render(<App />);
expect(ReactNoop.flush()).toEqual([
'Step: 0, Shadow: 0',
'Step: 1, Shadow: 0',
'Step: 2, Shadow: 0',
'Step: 3, Shadow: 0',
'Step: 4, Shadow: 0',
'Step: 5, Shadow: 0',
]);
expect(ReactNoop.getChildren()).toEqual([span(0)]);
act(() => dispatch());
expect(ReactNoop.flush()).toEqual(['Step: 5, Shadow: 5']);
expect(ReactNoop.getChildren()).toEqual([span(5)]);
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "react-test-renderer",
"version": "16.8.5",
"version": "16.8.6",
"description": "React package for snapshot testing.",
"main": "index.js",
"repository": {
@@ -21,8 +21,8 @@
"dependencies": {
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"react-is": "^16.8.5",
"scheduler": "^0.13.5"
"react-is": "^16.8.6",
"scheduler": "^0.13.6"
},
"peerDependencies": {
"react": "^16.0.0"

View File

@@ -4,7 +4,7 @@
"keywords": [
"react"
],
"version": "16.8.5",
"version": "16.8.6",
"homepage": "https://reactjs.org/",
"bugs": "https://github.com/facebook/react/issues",
"license": "MIT",
@@ -29,7 +29,7 @@
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.5"
"scheduler": "^0.13.6"
},
"browserify": {
"transform": [

View File

@@ -541,9 +541,10 @@ describe('ReactContextValidator', () => {
it('should warn if an invalid contextType is defined', () => {
const Context = React.createContext();
// This tests that both Context.Consumer and Context.Provider
// warn about invalid contextType.
class ComponentA extends React.Component {
static contextType = Context.Provider;
static contextType = Context.Consumer;
render() {
return <div />;
}
@@ -560,7 +561,7 @@ describe('ReactContextValidator', () => {
}).toWarnDev(
'Warning: ComponentA defines an invalid contextType. ' +
'contextType should point to the Context object returned by React.createContext(). ' +
'Did you accidentally pass the Context.Provider instead?',
'Did you accidentally pass the Context.Consumer instead?',
{withoutStack: true},
);
@@ -577,6 +578,87 @@ describe('ReactContextValidator', () => {
);
});
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(() => {
ReactTestUtils.renderIntoDocument(<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(() => {
ReactTestUtils.renderIntoDocument(<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(() => {
ReactTestUtils.renderIntoDocument(<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(() => {
ReactTestUtils.renderIntoDocument(<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},
);
});
it('should warn if you define contextType on a function component', () => {
const Context = React.createContext();

View File

@@ -1,6 +1,6 @@
{
"name": "scheduler",
"version": "0.13.5",
"version": "0.13.6",
"description": "Cooperative scheduler for the browser environment.",
"main": "index.js",
"repository": {

View File

@@ -8,4 +8,4 @@
'use strict';
// TODO: this is special because it gets imported during build.
module.exports = '16.8.5';
module.exports = '16.8.6';