Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a feature flag to disable legacy context #16269

Merged
merged 7 commits into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/

'use strict';

const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');

let React;
let ReactDOM;
let ReactFeatureFlags;
let ReactDOMServer;
let ReactTestUtils;

function initModules() {
// Reset warning cache.
jest.resetModuleRegistry();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');

ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.disableLegacyContext = true;

// Make them available to the helpers.
return {
ReactDOM,
ReactDOMServer,
ReactTestUtils,
};
}

const {resetModules, itRenders} = ReactDOMServerIntegrationUtils(initModules);

function formatValue(val) {
if (val === null) {
return 'null';
}
if (val === undefined) {
return 'undefined';
}
if (typeof val === 'string') {
return val;
}
return JSON.stringify(val);
}

describe('ReactDOMServerIntegrationLegacyContextDisabled', () => {
beforeEach(() => {
resetModules();
});

itRenders('undefined legacy context with warning', async render => {
class LegacyProvider extends React.Component {
static childContextTypes = {
foo() {},
};
getChildContext() {
return {foo: 10};
}
render() {
return this.props.children;
}
}

let lifecycleContextLog = [];
class LegacyClsConsumer extends React.Component {
static contextTypes = {
foo() {},
};
shouldComponentUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
return true;
}
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
lifecycleContextLog.push(nextContext);
}
UNSAFE_componentWillUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
}
render() {
return formatValue(this.context);
}
}

function LegacyFnConsumer(props, context) {
return formatValue(context);
}
LegacyFnConsumer.contextTypes = {foo() {}};

function RegularFn(props, context) {
return formatValue(context);
}

const e = await render(
<LegacyProvider>
<span>
<LegacyClsConsumer />
<LegacyFnConsumer />
<RegularFn />
</span>
</LegacyProvider>,
3,
);
expect(e.textContent).toBe('{}undefinedundefined');
expect(lifecycleContextLog).toEqual([]);
});

itRenders('modern context', async render => {
let Ctx = React.createContext();

class Provider extends React.Component {
render() {
return (
<Ctx.Provider value={this.props.value}>
{this.props.children}
</Ctx.Provider>
);
}
}

class RenderPropConsumer extends React.Component {
render() {
return <Ctx.Consumer>{value => formatValue(value)}</Ctx.Consumer>;
}
}

let lifecycleContextLog = [];
class ContextTypeConsumer extends React.Component {
static contextType = Ctx;
shouldComponentUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
return true;
}
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
lifecycleContextLog.push(nextContext);
}
UNSAFE_componentWillUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
}
render() {
return formatValue(this.context);
}
}

function FnConsumer() {
return formatValue(React.useContext(Ctx));
}

const e = await render(
<Provider value="a">
<span>
<RenderPropConsumer />
<ContextTypeConsumer />
<FnConsumer />
</span>
</Provider>,
);
expect(e.textContent).toBe('aaa');
expect(lifecycleContextLog).toEqual([]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/

'use strict';

let React;
let ReactDOM;
let ReactFeatureFlags;

describe('ReactLegacyContextDisabled', () => {
beforeEach(() => {
jest.resetModules();

React = require('react');
ReactDOM = require('react-dom');
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.disableLegacyContext = true;
});

function formatValue(val) {
if (val === null) {
return 'null';
}
if (val === undefined) {
return 'undefined';
}
if (typeof val === 'string') {
return val;
}
return JSON.stringify(val);
}

it('warns for legacy context', () => {
class LegacyProvider extends React.Component {
static childContextTypes = {
foo() {},
};
getChildContext() {
return {foo: 10};
}
render() {
return this.props.children;
}
}

let lifecycleContextLog = [];
class LegacyClsConsumer extends React.Component {
static contextTypes = {
foo() {},
};
shouldComponentUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
return true;
}
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
lifecycleContextLog.push(nextContext);
}
UNSAFE_componentWillUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
}
render() {
return formatValue(this.context);
}
}

function LegacyFnConsumer(props, context) {
return formatValue(context);
}
LegacyFnConsumer.contextTypes = {foo() {}};

function RegularFn(props, context) {
return formatValue(context);
}

const container = document.createElement('div');
expect(() => {
ReactDOM.render(
<LegacyProvider>
<span>
<LegacyClsConsumer />
<LegacyFnConsumer />
<RegularFn />
</span>
</LegacyProvider>,
container,
);
}).toWarnDev(
[
'LegacyProvider uses the legacy childContextTypes API which is no longer supported. ' +
'Use React.createContext() instead.',
'LegacyClsConsumer uses the legacy contextTypes API which is no longer supported. ' +
'Use React.createContext() with static contextType instead.',
'LegacyFnConsumer uses the legacy contextTypes API which is no longer supported. ' +
'Use React.createContext() with React.useContext() instead.',
],
{withoutStack: true},
);
expect(container.textContent).toBe('{}undefinedundefined');
expect(lifecycleContextLog).toEqual([]);

// Test update path.
ReactDOM.render(
<LegacyProvider>
<span>
<LegacyClsConsumer />
<LegacyFnConsumer />
<RegularFn />
</span>
</LegacyProvider>,
container,
);
expect(container.textContent).toBe('{}undefinedundefined');
expect(lifecycleContextLog).toEqual([{}, {}, {}]);
ReactDOM.unmountComponentAtNode(container);
});

it('renders a tree with modern context', () => {
let Ctx = React.createContext();

class Provider extends React.Component {
render() {
return (
<Ctx.Provider value={this.props.value}>
{this.props.children}
</Ctx.Provider>
);
}
}

class RenderPropConsumer extends React.Component {
render() {
return <Ctx.Consumer>{value => formatValue(value)}</Ctx.Consumer>;
}
}

let lifecycleContextLog = [];
class ContextTypeConsumer extends React.Component {
static contextType = Ctx;
shouldComponentUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
return true;
}
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
lifecycleContextLog.push(nextContext);
}
UNSAFE_componentWillUpdate(nextProps, nextState, nextContext) {
lifecycleContextLog.push(nextContext);
}
render() {
return formatValue(this.context);
}
}

function FnConsumer() {
return formatValue(React.useContext(Ctx));
}

const container = document.createElement('div');
ReactDOM.render(
<Provider value="a">
<span>
<RenderPropConsumer />
<ContextTypeConsumer />
<FnConsumer />
</span>
</Provider>,
container,
);
expect(container.textContent).toBe('aaa');
expect(lifecycleContextLog).toEqual([]);

// Test update path
ReactDOM.render(
<Provider value="a">
<span>
<RenderPropConsumer />
<ContextTypeConsumer />
<FnConsumer />
</span>
</Provider>,
container,
);
expect(container.textContent).toBe('aaa');
expect(lifecycleContextLog).toEqual(['a', 'a', 'a']);
lifecycleContextLog.length = 0;

ReactDOM.render(
<Provider value="b">
<span>
<RenderPropConsumer />
<ContextTypeConsumer />
<FnConsumer />
</span>
</Provider>,
container,
);
expect(container.textContent).toBe('bbb');
expect(lifecycleContextLog).toEqual(['b', 'b']); // sCU skipped due to changed context value.
ReactDOM.unmountComponentAtNode(container);
});
});
Loading