Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a015b68cc | ||
|
|
c3b2839641 | ||
|
|
d4ea75dc42 | ||
|
|
9090712fd3 | ||
|
|
7548c019ce | ||
|
|
415ee0e6ea | ||
|
|
589423270e | ||
|
|
73bfaa16e1 | ||
|
|
c2a246e956 | ||
|
|
2cfb4741fd |
@@ -529,21 +529,24 @@ workflows:
|
||||
# - "-r=www-modern --env=production --variant=true"
|
||||
|
||||
# TODO: Test more persistent configurations?
|
||||
- download_base_build_for_sizebot:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- main
|
||||
requires:
|
||||
- setup
|
||||
- sizebot:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- main
|
||||
requires:
|
||||
- download_base_build_for_sizebot
|
||||
- yarn_build_combined
|
||||
|
||||
# Sizebot is disabled because the base revision of this 18.3 branch is
|
||||
# too old
|
||||
# - download_base_build_for_sizebot:
|
||||
# filters:
|
||||
# branches:
|
||||
# ignore:
|
||||
# - main
|
||||
# requires:
|
||||
# - setup
|
||||
# - sizebot:
|
||||
# filters:
|
||||
# branches:
|
||||
# ignore:
|
||||
# - main
|
||||
# requires:
|
||||
# - download_base_build_for_sizebot
|
||||
# - yarn_build_combined
|
||||
- yarn_lint_build:
|
||||
requires:
|
||||
- yarn_build_combined
|
||||
|
||||
@@ -18,25 +18,25 @@
|
||||
//
|
||||
// 0.0.0-experimental-241c4467e-20200129
|
||||
|
||||
const ReactVersion = '18.2.0';
|
||||
const ReactVersion = '18.3.0';
|
||||
|
||||
// The label used by the @next channel. Represents the upcoming release's
|
||||
// stability. Could be "alpha", "beta", "rc", etc.
|
||||
const nextChannelLabel = 'next';
|
||||
|
||||
const stablePackages = {
|
||||
'eslint-plugin-react-hooks': '4.6.0',
|
||||
'jest-react': '0.14.0',
|
||||
'eslint-plugin-react-hooks': '4.6.1',
|
||||
'jest-react': '0.14.1',
|
||||
react: ReactVersion,
|
||||
'react-art': ReactVersion,
|
||||
'react-dom': ReactVersion,
|
||||
'react-is': ReactVersion,
|
||||
'react-reconciler': '0.29.0',
|
||||
'react-refresh': '0.14.0',
|
||||
'react-reconciler': '0.29.1',
|
||||
'react-refresh': '0.14.1',
|
||||
'react-test-renderer': ReactVersion,
|
||||
'use-subscription': '1.8.0',
|
||||
'use-sync-external-store': '1.2.0',
|
||||
scheduler: '0.23.0',
|
||||
'use-subscription': '1.8.1',
|
||||
'use-sync-external-store': '1.2.1',
|
||||
scheduler: '0.23.1',
|
||||
};
|
||||
|
||||
// These packages do not exist in the @next or @latest channel, only
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"homepage": "https://reactjs.org/",
|
||||
"peerDependencies": {
|
||||
"jest": "^23.0.1 || ^24.0.0 || ^25.1.0",
|
||||
"react": "^18.1.0",
|
||||
"react-test-renderer": "^18.1.0"
|
||||
"react": "^18.3.0",
|
||||
"react-test-renderer": "^18.3.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -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": "18.1.0",
|
||||
"version": "18.3.0",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -28,7 +28,7 @@
|
||||
"scheduler": "^0.22.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.1.0"
|
||||
"react": "^18.3.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -882,7 +882,11 @@ describe('ReactHooksInspectionIntegration', () => {
|
||||
|
||||
await LazyFoo;
|
||||
|
||||
Scheduler.unstable_flushAll();
|
||||
expect(() => {
|
||||
Scheduler.unstable_flushAll();
|
||||
}).toErrorDev([
|
||||
'Foo: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
]);
|
||||
|
||||
const childFiber = renderer.root._currentFiber();
|
||||
const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-dom",
|
||||
"version": "18.1.0",
|
||||
"version": "18.3.0",
|
||||
"description": "React package for working with the DOM.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -21,7 +21,7 @@
|
||||
"scheduler": "^0.22.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.1.0"
|
||||
"react": "^18.3.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMServer;
|
||||
let ReactFeatureFlags;
|
||||
let ReactTestUtils;
|
||||
|
||||
describe('ReactComponent', () => {
|
||||
@@ -21,6 +22,7 @@ describe('ReactComponent', () => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
@@ -36,7 +38,7 @@ describe('ReactComponent', () => {
|
||||
}).toThrowError(/Target container is not a DOM element./);
|
||||
});
|
||||
|
||||
it('should throw when supplying a ref outside of render method', () => {
|
||||
it('should throw when supplying a string ref outside of render method', () => {
|
||||
let instance = <div ref="badDiv" />;
|
||||
expect(function() {
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
@@ -102,7 +104,7 @@ describe('ReactComponent', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should support refs on owned components', () => {
|
||||
it('should support string refs on owned components', () => {
|
||||
const innerObj = {};
|
||||
const outerObj = {};
|
||||
|
||||
@@ -133,10 +135,29 @@ describe('ReactComponent', () => {
|
||||
}
|
||||
}
|
||||
|
||||
ReactTestUtils.renderIntoDocument(<Component />);
|
||||
expect(() => {
|
||||
ReactTestUtils.renderIntoDocument(<Component />);
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "div" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in div (at **)\n' +
|
||||
' in Wrapper (at **)\n' +
|
||||
' in Component (at **)',
|
||||
'Warning: Component "Component" contains the string ref "outer". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Component (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
});
|
||||
|
||||
it('should not have refs on unmounted components', () => {
|
||||
it('should not have string refs on unmounted components', () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
|
||||
@@ -378,7 +378,7 @@ describe('ReactComponentLifeCycle', () => {
|
||||
}
|
||||
// you would *NEVER* do anything like this in real code!
|
||||
this.state.hasRenderCompleted = true;
|
||||
return <div ref="theDiv">I am the inner DIV</div>;
|
||||
return <div ref={React.createRef()}>I am the inner DIV</div>;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -477,7 +477,9 @@ describe('ReactComponentLifeCycle', () => {
|
||||
class Component extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Tooltip ref="tooltip" tooltip={<div>{this.props.tooltipText}</div>}>
|
||||
<Tooltip
|
||||
ref={React.createRef()}
|
||||
tooltip={<div>{this.props.tooltipText}</div>}>
|
||||
{this.props.text}
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -72,6 +72,8 @@ describe('ReactCompositeComponent', () => {
|
||||
MorphingComponent = class extends React.Component {
|
||||
state = {activated: false};
|
||||
|
||||
xRef = React.createRef();
|
||||
|
||||
_toggleActivatedState = () => {
|
||||
this.setState({activated: !this.state.activated});
|
||||
};
|
||||
@@ -79,9 +81,9 @@ describe('ReactCompositeComponent', () => {
|
||||
render() {
|
||||
const toggleActivatedState = this._toggleActivatedState;
|
||||
return !this.state.activated ? (
|
||||
<a ref="x" onClick={toggleActivatedState} />
|
||||
<a ref={this.xRef} onClick={toggleActivatedState} />
|
||||
) : (
|
||||
<b ref="x" onClick={toggleActivatedState} />
|
||||
<b ref={this.xRef} onClick={toggleActivatedState} />
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -91,14 +93,16 @@ describe('ReactCompositeComponent', () => {
|
||||
* reallocated again.
|
||||
*/
|
||||
ChildUpdates = class extends React.Component {
|
||||
anchorRef = React.createRef();
|
||||
|
||||
getAnchor = () => {
|
||||
return this.refs.anch;
|
||||
return this.anchorRef.current;
|
||||
};
|
||||
|
||||
render() {
|
||||
const className = this.props.anchorClassOn ? 'anchorClass' : '';
|
||||
return this.props.renderAnchor ? (
|
||||
<a ref="anch" className={className} />
|
||||
<a ref={this.anchorRef} className={className} />
|
||||
) : (
|
||||
<b />
|
||||
);
|
||||
@@ -186,11 +190,11 @@ describe('ReactCompositeComponent', () => {
|
||||
it('should rewire refs when rendering to different child types', () => {
|
||||
const instance = ReactTestUtils.renderIntoDocument(<MorphingComponent />);
|
||||
|
||||
expect(instance.refs.x.tagName).toBe('A');
|
||||
expect(instance.xRef.current.tagName).toBe('A');
|
||||
instance._toggleActivatedState();
|
||||
expect(instance.refs.x.tagName).toBe('B');
|
||||
expect(instance.xRef.current.tagName).toBe('B');
|
||||
instance._toggleActivatedState();
|
||||
expect(instance.refs.x.tagName).toBe('A');
|
||||
expect(instance.xRef.current.tagName).toBe('A');
|
||||
});
|
||||
|
||||
it('should not cache old DOM nodes when switching constructors', () => {
|
||||
@@ -739,10 +743,13 @@ describe('ReactCompositeComponent', () => {
|
||||
}
|
||||
|
||||
class Wrapper extends React.Component {
|
||||
parentRef = React.createRef();
|
||||
childRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Parent ref="parent">
|
||||
<Child ref="child" />
|
||||
<Parent ref={this.parentRef}>
|
||||
<Child ref={this.childRef} />
|
||||
</Parent>
|
||||
);
|
||||
}
|
||||
@@ -750,14 +757,14 @@ describe('ReactCompositeComponent', () => {
|
||||
|
||||
const wrapper = ReactTestUtils.renderIntoDocument(<Wrapper />);
|
||||
|
||||
expect(wrapper.refs.parent.state.flag).toEqual(true);
|
||||
expect(wrapper.refs.child.context).toEqual({flag: true});
|
||||
expect(wrapper.parentRef.current.state.flag).toEqual(true);
|
||||
expect(wrapper.childRef.current.context).toEqual({flag: true});
|
||||
|
||||
// We update <Parent /> while <Child /> is still a static prop relative to this update
|
||||
wrapper.refs.parent.setState({flag: false});
|
||||
wrapper.parentRef.current.setState({flag: false});
|
||||
|
||||
expect(wrapper.refs.parent.state.flag).toEqual(false);
|
||||
expect(wrapper.refs.child.context).toEqual({flag: false});
|
||||
expect(wrapper.parentRef.current.state.flag).toEqual(false);
|
||||
expect(wrapper.childRef.current.context).toEqual({flag: false});
|
||||
});
|
||||
|
||||
it('should pass context transitively', () => {
|
||||
@@ -1142,14 +1149,17 @@ describe('ReactCompositeComponent', () => {
|
||||
}
|
||||
|
||||
class Component extends React.Component {
|
||||
static0Ref = React.createRef();
|
||||
static1Ref = React.createRef();
|
||||
|
||||
render() {
|
||||
if (this.props.flipped) {
|
||||
return (
|
||||
<div>
|
||||
<Static ref="static0" key="B">
|
||||
<Static ref={this.static0Ref} key="B">
|
||||
B (ignored)
|
||||
</Static>
|
||||
<Static ref="static1" key="A">
|
||||
<Static ref={this.static1Ref} key="A">
|
||||
A (ignored)
|
||||
</Static>
|
||||
</div>
|
||||
@@ -1157,10 +1167,10 @@ describe('ReactCompositeComponent', () => {
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<Static ref="static0" key="A">
|
||||
<Static ref={this.static0Ref} key="A">
|
||||
A
|
||||
</Static>
|
||||
<Static ref="static1" key="B">
|
||||
<Static ref={this.static1Ref} key="B">
|
||||
B
|
||||
</Static>
|
||||
</div>
|
||||
@@ -1171,14 +1181,14 @@ describe('ReactCompositeComponent', () => {
|
||||
|
||||
const container = document.createElement('div');
|
||||
const comp = ReactDOM.render(<Component flipped={false} />, container);
|
||||
expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('A');
|
||||
expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('B');
|
||||
expect(ReactDOM.findDOMNode(comp.static0Ref.current).textContent).toBe('A');
|
||||
expect(ReactDOM.findDOMNode(comp.static1Ref.current).textContent).toBe('B');
|
||||
|
||||
// When flipping the order, the refs should update even though the actual
|
||||
// contents do not
|
||||
ReactDOM.render(<Component flipped={true} />, container);
|
||||
expect(ReactDOM.findDOMNode(comp.refs.static0).textContent).toBe('B');
|
||||
expect(ReactDOM.findDOMNode(comp.refs.static1).textContent).toBe('A');
|
||||
expect(ReactDOM.findDOMNode(comp.static0Ref.current).textContent).toBe('B');
|
||||
expect(ReactDOM.findDOMNode(comp.static1Ref.current).textContent).toBe('A');
|
||||
});
|
||||
|
||||
it('should allow access to findDOMNode in componentWillUnmount', () => {
|
||||
@@ -1453,10 +1463,11 @@ describe('ReactCompositeComponent', () => {
|
||||
this.state = {
|
||||
color: 'green',
|
||||
};
|
||||
this.appleRef = React.createRef();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Apple color={this.state.color} ref="apple" />;
|
||||
return <Apple color={this.state.color} ref={this.appleRef} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1502,15 +1513,15 @@ describe('ReactCompositeComponent', () => {
|
||||
expect(renderCalls).toBe(2);
|
||||
|
||||
// Re-render base on state
|
||||
instance.refs.apple.cut();
|
||||
instance.appleRef.current.cut();
|
||||
expect(renderCalls).toBe(3);
|
||||
|
||||
// No re-render based on state
|
||||
instance.refs.apple.cut();
|
||||
instance.appleRef.current.cut();
|
||||
expect(renderCalls).toBe(3);
|
||||
|
||||
// Re-render based on state again
|
||||
instance.refs.apple.eatSlice();
|
||||
instance.appleRef.current.eatSlice();
|
||||
expect(renderCalls).toBe(4);
|
||||
});
|
||||
|
||||
|
||||
@@ -193,12 +193,13 @@ describe('ReactDOMEventListener', () => {
|
||||
const onMouseOut = event => mouseOut(event.target);
|
||||
|
||||
class Wrapper extends React.Component {
|
||||
innerRef = React.createRef();
|
||||
getInner = () => {
|
||||
return this.refs.inner;
|
||||
return this.innerRef.current;
|
||||
};
|
||||
|
||||
render() {
|
||||
const inner = <div ref="inner">Inner</div>;
|
||||
const inner = <div ref={this.innerRef}>Inner</div>;
|
||||
return (
|
||||
<div>
|
||||
<div onMouseOut={onMouseOut} id="outer">
|
||||
|
||||
@@ -264,6 +264,7 @@ describe('ReactDOMFizzServer', () => {
|
||||
}
|
||||
|
||||
// @gate experimental
|
||||
// @gate !warnAboutDefaultPropsOnFunctionComponents || !__DEV__
|
||||
it('should asynchronously load a lazy component', async () => {
|
||||
let resolveA;
|
||||
const LazyA = React.lazy(() => {
|
||||
|
||||
@@ -16,6 +16,8 @@ let usingPartialRenderer;
|
||||
const util = require('util');
|
||||
const realConsoleError = console.error;
|
||||
|
||||
const shouldIgnoreConsoleError = require('../../../../scripts/jest/shouldIgnoreConsoleError');
|
||||
|
||||
describe('ReactDOMServerHydration', () => {
|
||||
let container;
|
||||
|
||||
@@ -57,6 +59,9 @@ describe('ReactDOMServerHydration', () => {
|
||||
// We only want console errors in this suite.
|
||||
return null;
|
||||
}
|
||||
if (shouldIgnoreConsoleError(format, ...rest)) {
|
||||
return null;
|
||||
}
|
||||
rest[rest.length - 1] = normalizeCodeLocInfo(rest[rest.length - 1]);
|
||||
return util.format(format, ...rest);
|
||||
}
|
||||
|
||||
@@ -1071,22 +1071,31 @@ describe('ReactDOMInput', () => {
|
||||
|
||||
it('should control radio buttons', () => {
|
||||
class RadioGroup extends React.Component {
|
||||
aRef = React.createRef();
|
||||
bRef = React.createRef();
|
||||
cRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
ref="a"
|
||||
ref={this.aRef}
|
||||
type="radio"
|
||||
name="fruit"
|
||||
checked={true}
|
||||
onChange={emptyFunction}
|
||||
/>
|
||||
A
|
||||
<input ref="b" type="radio" name="fruit" onChange={emptyFunction} />
|
||||
<input
|
||||
ref={this.bRef}
|
||||
type="radio"
|
||||
name="fruit"
|
||||
onChange={emptyFunction}
|
||||
/>
|
||||
B
|
||||
<form>
|
||||
<input
|
||||
ref="c"
|
||||
ref={this.cRef}
|
||||
type="radio"
|
||||
name="fruit"
|
||||
defaultChecked={true}
|
||||
@@ -1099,9 +1108,9 @@ describe('ReactDOMInput', () => {
|
||||
}
|
||||
|
||||
const stub = ReactDOM.render(<RadioGroup />, container);
|
||||
const aNode = stub.refs.a;
|
||||
const bNode = stub.refs.b;
|
||||
const cNode = stub.refs.c;
|
||||
const aNode = stub.aRef.current;
|
||||
const bNode = stub.bRef.current;
|
||||
const cNode = stub.cRef.current;
|
||||
|
||||
expect(aNode.checked).toBe(true);
|
||||
expect(bNode.checked).toBe(false);
|
||||
|
||||
@@ -337,7 +337,7 @@ describe('ReactDOMServerIntegration', () => {
|
||||
itRenders('no ref attribute', async render => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
return <div ref="foo" />;
|
||||
return <div ref={React.createRef()} />;
|
||||
}
|
||||
}
|
||||
const e = await render(<RefComponent />);
|
||||
|
||||
@@ -14,6 +14,7 @@ const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegratio
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMServer;
|
||||
let ReactFeatureFlags;
|
||||
let ReactTestUtils;
|
||||
|
||||
function initModules() {
|
||||
@@ -22,6 +23,7 @@ function initModules() {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
// Make them available to the helpers.
|
||||
@@ -91,10 +93,22 @@ describe('ReactDOMServerIntegration', () => {
|
||||
root.innerHTML = markup;
|
||||
let component = null;
|
||||
resetModules();
|
||||
await asyncReactDOMRender(
|
||||
<RefsComponent ref={e => (component = e)} />,
|
||||
root,
|
||||
true,
|
||||
await expect(async () => {
|
||||
await asyncReactDOMRender(
|
||||
<RefsComponent ref={e => (component = e)} />,
|
||||
root,
|
||||
true,
|
||||
);
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "RefsComponent" contains the string ref "myDiv". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in RefsComponent (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
expect(component.refs.myDiv).toBe(root.firstChild);
|
||||
});
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
let ReactNoop;
|
||||
let Scheduler;
|
||||
let JSXDEVRuntime;
|
||||
@@ -19,19 +18,11 @@ describe('ReactDeprecationWarnings', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
Scheduler = require('scheduler');
|
||||
if (__DEV__) {
|
||||
JSXDEVRuntime = require('react/jsx-dev-runtime');
|
||||
}
|
||||
ReactFeatureFlags.warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
ReactFeatureFlags.warnAboutStringRefs = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
ReactFeatureFlags.warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
ReactFeatureFlags.warnAboutStringRefs = false;
|
||||
});
|
||||
|
||||
it('should warn when given defaultProps', () => {
|
||||
@@ -51,6 +42,27 @@ describe('ReactDeprecationWarnings', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn when given defaultProps on a memoized function', () => {
|
||||
const MemoComponent = React.memo(function FunctionalComponent(props) {
|
||||
return null;
|
||||
});
|
||||
|
||||
MemoComponent.defaultProps = {
|
||||
testProp: true,
|
||||
};
|
||||
|
||||
ReactNoop.render(
|
||||
<div>
|
||||
<MemoComponent />
|
||||
</div>,
|
||||
);
|
||||
expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
|
||||
'Warning: FunctionalComponent: Support for defaultProps ' +
|
||||
'will be removed from memo components in a future major ' +
|
||||
'release. Use JavaScript default parameters instead.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn when given string refs', () => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
@@ -74,9 +86,7 @@ describe('ReactDeprecationWarnings', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not warn when owner and self are the same for string refs', () => {
|
||||
ReactFeatureFlags.warnAboutStringRefs = false;
|
||||
|
||||
it('should warn when owner and self are the same for string refs', () => {
|
||||
class RefComponent extends React.Component {
|
||||
render() {
|
||||
return null;
|
||||
@@ -87,7 +97,11 @@ describe('ReactDeprecationWarnings', () => {
|
||||
return <RefComponent ref="refComponent" __self={this} />;
|
||||
}
|
||||
}
|
||||
ReactNoop.renderLegacySyncRoot(<Component />);
|
||||
expect(() => {
|
||||
ReactNoop.renderLegacySyncRoot(<Component />);
|
||||
}).toErrorDev([
|
||||
'Component "Component" contains the string ref "refComponent". Support for string refs will be removed in a future major release.',
|
||||
]);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
});
|
||||
|
||||
@@ -367,11 +367,12 @@ describe('ReactFunctionComponent', () => {
|
||||
Child.defaultProps = {test: 2};
|
||||
Child.propTypes = {test: PropTypes.string};
|
||||
|
||||
expect(() => ReactTestUtils.renderIntoDocument(<Child />)).toErrorDev(
|
||||
expect(() => ReactTestUtils.renderIntoDocument(<Child />)).toErrorDev([
|
||||
'Warning: Child: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
|
||||
'supplied to `Child`, expected `string`.\n' +
|
||||
' in Child (at **)',
|
||||
);
|
||||
]);
|
||||
});
|
||||
|
||||
it('should receive context', () => {
|
||||
|
||||
@@ -67,17 +67,18 @@ describe('ReactIdentity', () => {
|
||||
|
||||
function renderAComponentWithKeyIntoContainer(key, container) {
|
||||
class Wrapper extends React.Component {
|
||||
spanRef = React.createRef();
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<span ref="span" key={key} />
|
||||
<span ref={this.spanRef} key={key} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const instance = ReactDOM.render(<Wrapper />, container);
|
||||
const span = instance.refs.span;
|
||||
const span = instance.spanRef.current;
|
||||
expect(span).not.toBe(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ class StatusDisplay extends React.Component {
|
||||
* Displays friends statuses.
|
||||
*/
|
||||
class FriendsStatusDisplay extends React.Component {
|
||||
displays = {};
|
||||
|
||||
/**
|
||||
* Gets the order directly from each rendered child's `index` field.
|
||||
* Refs are not maintained in the rendered order, and neither is
|
||||
@@ -84,7 +86,7 @@ class FriendsStatusDisplay extends React.Component {
|
||||
const originalKeys = this.getOriginalKeys();
|
||||
for (let i = 0; i < originalKeys.length; i++) {
|
||||
const key = originalKeys[i];
|
||||
res[key] = this.refs[key];
|
||||
res[key] = this.displays[key];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -104,7 +106,7 @@ class FriendsStatusDisplay extends React.Component {
|
||||
// We are only interested in children up to the current key.
|
||||
return;
|
||||
}
|
||||
expect(this.refs[key]).toBeTruthy();
|
||||
expect(this.displays[key]).toBeTruthy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +118,9 @@ class FriendsStatusDisplay extends React.Component {
|
||||
!status ? null : (
|
||||
<StatusDisplay
|
||||
key={key}
|
||||
ref={key}
|
||||
ref={current => {
|
||||
this.displays[key] = current;
|
||||
}}
|
||||
contentKey={key}
|
||||
onFlush={this.verifyPreviousRefsResolved.bind(this, key)}
|
||||
status={status}
|
||||
|
||||
@@ -620,17 +620,27 @@ describe('ReactDOMServer', () => {
|
||||
describe('renderToStaticNodeStream', () => {
|
||||
it('should generate simple markup', () => {
|
||||
const SuccessfulElement = React.createElement(() => <img />);
|
||||
const response = ReactDOMServer.renderToStaticNodeStream(
|
||||
SuccessfulElement,
|
||||
);
|
||||
expect(response.read().toString()).toMatch(new RegExp('<img' + '/>'));
|
||||
expect(() => {
|
||||
const response = ReactDOMServer.renderToStaticNodeStream(
|
||||
SuccessfulElement,
|
||||
);
|
||||
expect(response.read().toString()).toMatch(new RegExp('<img' + '/>'));
|
||||
}).toErrorDev('ReactDOMServer.renderToStaticNodeStream() is deprecated', {
|
||||
withoutStack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle errors correctly', () => {
|
||||
const FailingElement = React.createElement(() => {
|
||||
throw new Error('An Error');
|
||||
});
|
||||
const response = ReactDOMServer.renderToStaticNodeStream(FailingElement);
|
||||
|
||||
let response;
|
||||
expect(() => {
|
||||
response = ReactDOMServer.renderToStaticNodeStream(FailingElement);
|
||||
}).toErrorDev('ReactDOMServer.renderToStaticNodeStream() is deprecated', {
|
||||
withoutStack: true,
|
||||
});
|
||||
return new Promise(resolve => {
|
||||
response.once('error', () => {
|
||||
resolve();
|
||||
|
||||
@@ -32,6 +32,8 @@ describe('ReactDOMServerHydration', () => {
|
||||
let numClicks = 0;
|
||||
|
||||
class TestComponent extends React.Component {
|
||||
spanRef = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
mountCount++;
|
||||
}
|
||||
@@ -42,7 +44,7 @@ describe('ReactDOMServerHydration', () => {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<span ref="span" onClick={this.click}>
|
||||
<span ref={this.spanRef} onClick={this.click}>
|
||||
Name: {this.props.name}
|
||||
</span>
|
||||
);
|
||||
@@ -85,7 +87,7 @@ describe('ReactDOMServerHydration', () => {
|
||||
|
||||
// Ensure the events system works after mount into server markup
|
||||
expect(numClicks).toEqual(0);
|
||||
instance.refs.span.click();
|
||||
instance.spanRef.current.click();
|
||||
expect(numClicks).toEqual(1);
|
||||
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
@@ -103,7 +105,7 @@ describe('ReactDOMServerHydration', () => {
|
||||
|
||||
// Ensure the events system works after markup mismatch.
|
||||
expect(numClicks).toEqual(1);
|
||||
instance.refs.span.click();
|
||||
instance.spanRef.current.click();
|
||||
expect(numClicks).toEqual(2);
|
||||
} finally {
|
||||
document.body.removeChild(element);
|
||||
|
||||
@@ -222,13 +222,17 @@ describe('ReactTestUtils', () => {
|
||||
// Full-page components (html, head, body) can't be rendered into a div
|
||||
// directly...
|
||||
class Root extends React.Component {
|
||||
htmlRef = React.createRef();
|
||||
headRef = React.createRef();
|
||||
bodyRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<html ref="html">
|
||||
<head ref="head">
|
||||
<html ref={this.htmlRef}>
|
||||
<head ref={this.headRef}>
|
||||
<title>hello</title>
|
||||
</head>
|
||||
<body ref="body">hello, world</body>
|
||||
<body ref={this.bodyRef}>hello, world</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -238,12 +242,12 @@ describe('ReactTestUtils', () => {
|
||||
const testDocument = getTestDocument(markup);
|
||||
const component = ReactDOM.hydrate(<Root />, testDocument);
|
||||
|
||||
expect(component.refs.html.tagName).toBe('HTML');
|
||||
expect(component.refs.head.tagName).toBe('HEAD');
|
||||
expect(component.refs.body.tagName).toBe('BODY');
|
||||
expect(ReactTestUtils.isDOMComponent(component.refs.html)).toBe(true);
|
||||
expect(ReactTestUtils.isDOMComponent(component.refs.head)).toBe(true);
|
||||
expect(ReactTestUtils.isDOMComponent(component.refs.body)).toBe(true);
|
||||
expect(component.htmlRef.current.tagName).toBe('HTML');
|
||||
expect(component.headRef.current.tagName).toBe('HEAD');
|
||||
expect(component.bodyRef.current.tagName).toBe('BODY');
|
||||
expect(ReactTestUtils.isDOMComponent(component.htmlRef.current)).toBe(true);
|
||||
expect(ReactTestUtils.isDOMComponent(component.headRef.current)).toBe(true);
|
||||
expect(ReactTestUtils.isDOMComponent(component.bodyRef.current)).toBe(true);
|
||||
});
|
||||
|
||||
it('can scry with stateless components involved', () => {
|
||||
@@ -349,12 +353,13 @@ describe('ReactTestUtils', () => {
|
||||
|
||||
it('should change the value of an input field in a component', () => {
|
||||
class SomeComponent extends React.Component {
|
||||
inputRef = React.createRef();
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
ref="input"
|
||||
ref={this.inputRef}
|
||||
onChange={this.props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
@@ -374,7 +379,7 @@ describe('ReactTestUtils', () => {
|
||||
container,
|
||||
);
|
||||
|
||||
const node = instance.refs.input;
|
||||
const node = instance.inputRef.current;
|
||||
node.value = 'zebra';
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
|
||||
|
||||
@@ -493,8 +493,11 @@ function runActTests(label, render, unmount, rerender) {
|
||||
// it's annoying that we have to wait a tick before this warning comes in
|
||||
await sleep(0);
|
||||
if (__DEV__) {
|
||||
expect(console.error.calls.count()).toEqual(1);
|
||||
expect(console.error.calls.count()).toEqual(2);
|
||||
expect(console.error.calls.argsFor(0)[0]).toMatch(
|
||||
'`ReactDOMTestUtils.act` is deprecated ',
|
||||
);
|
||||
expect(console.error.calls.argsFor(1)[0]).toMatch(
|
||||
'You called act(async () => ...) without await.',
|
||||
);
|
||||
}
|
||||
@@ -516,13 +519,16 @@ function runActTests(label, render, unmount, rerender) {
|
||||
|
||||
await sleep(150);
|
||||
if (__DEV__) {
|
||||
expect(console.error).toHaveBeenCalledTimes(2);
|
||||
expect(console.error).toHaveBeenCalledTimes(3);
|
||||
expect(console.error.calls.argsFor(0)[0]).toMatch(
|
||||
'You seem to have overlapping act() calls',
|
||||
'`ReactDOMTestUtils.act` is deprecated ',
|
||||
);
|
||||
expect(console.error.calls.argsFor(1)[0]).toMatch(
|
||||
'You seem to have overlapping act() calls',
|
||||
);
|
||||
expect(console.error.calls.argsFor(2)[0]).toMatch(
|
||||
'You seem to have overlapping act() calls',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ describe('ReactUpdates', () => {
|
||||
|
||||
class Parent extends React.Component {
|
||||
state = {x: 0};
|
||||
childRef = React.createRef();
|
||||
|
||||
componentDidUpdate() {
|
||||
parentUpdateCount++;
|
||||
@@ -155,7 +156,7 @@ describe('ReactUpdates', () => {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Child ref="child" x={this.state.x} />
|
||||
<Child ref={this.childRef} x={this.state.x} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -176,7 +177,7 @@ describe('ReactUpdates', () => {
|
||||
}
|
||||
|
||||
const instance = ReactTestUtils.renderIntoDocument(<Parent />);
|
||||
const child = instance.refs.child;
|
||||
const child = instance.childRef.current;
|
||||
expect(instance.state.x).toBe(0);
|
||||
expect(child.state.y).toBe(0);
|
||||
|
||||
@@ -200,6 +201,7 @@ describe('ReactUpdates', () => {
|
||||
|
||||
class Parent extends React.Component {
|
||||
state = {x: 0};
|
||||
childRef = React.createRef();
|
||||
|
||||
componentDidUpdate() {
|
||||
parentUpdateCount++;
|
||||
@@ -208,7 +210,7 @@ describe('ReactUpdates', () => {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Child ref="child" x={this.state.x} />
|
||||
<Child ref={this.childRef} x={this.state.x} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -229,7 +231,7 @@ describe('ReactUpdates', () => {
|
||||
}
|
||||
|
||||
const instance = ReactTestUtils.renderIntoDocument(<Parent />);
|
||||
const child = instance.refs.child;
|
||||
const child = instance.childRef.current;
|
||||
expect(instance.state.x).toBe(0);
|
||||
expect(child.state.y).toBe(0);
|
||||
|
||||
@@ -336,13 +338,15 @@ describe('ReactUpdates', () => {
|
||||
let childRenderCount = 0;
|
||||
|
||||
class Parent extends React.Component {
|
||||
childRef = React.createRef();
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
parentRenderCount++;
|
||||
return <Child ref="child" />;
|
||||
return <Child ref={this.childRef} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,7 +374,7 @@ describe('ReactUpdates', () => {
|
||||
expect(childRenderCount).toBe(1);
|
||||
|
||||
ReactDOM.unstable_batchedUpdates(function() {
|
||||
instance.refs.child.setState({x: 1});
|
||||
instance.childRef.current.setState({x: 1});
|
||||
});
|
||||
|
||||
expect(parentRenderCount).toBe(1);
|
||||
@@ -428,28 +432,34 @@ describe('ReactUpdates', () => {
|
||||
};
|
||||
|
||||
class Box extends React.Component {
|
||||
boxDivRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return <div ref="boxDiv">{this.props.children}</div>;
|
||||
return <div ref={this.boxDivRef}>{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
Object.assign(Box.prototype, UpdateLoggingMixin);
|
||||
|
||||
class Child extends React.Component {
|
||||
spanRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return <span ref="span">child</span>;
|
||||
return <span ref={this.spanRef}>child</span>;
|
||||
}
|
||||
}
|
||||
Object.assign(Child.prototype, UpdateLoggingMixin);
|
||||
|
||||
class Switcher extends React.Component {
|
||||
state = {tabKey: 'hello'};
|
||||
boxRef = React.createRef();
|
||||
switcherDivRef = React.createRef();
|
||||
render() {
|
||||
const child = this.props.children;
|
||||
|
||||
return (
|
||||
<Box ref="box">
|
||||
<Box ref={this.boxRef}>
|
||||
<div
|
||||
ref="switcherDiv"
|
||||
ref={this.switcherDivRef}
|
||||
style={{
|
||||
display: this.state.tabKey === child.key ? '' : 'none',
|
||||
}}>
|
||||
@@ -462,10 +472,13 @@ describe('ReactUpdates', () => {
|
||||
Object.assign(Switcher.prototype, UpdateLoggingMixin);
|
||||
|
||||
class App extends React.Component {
|
||||
switcherRef = React.createRef();
|
||||
childRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Switcher ref="switcher">
|
||||
<Child key="hello" ref="child" />
|
||||
<Switcher ref={this.switcherRef}>
|
||||
<Child key="hello" ref={this.childRef} />
|
||||
</Switcher>
|
||||
);
|
||||
}
|
||||
@@ -513,21 +526,21 @@ describe('ReactUpdates', () => {
|
||||
expectUpdates(desiredWillUpdates, desiredDidUpdates);
|
||||
}
|
||||
testUpdates(
|
||||
[root.refs.switcher.refs.box, root.refs.switcher],
|
||||
[root.switcherRef.current.boxRef.current, root.switcherRef.current],
|
||||
// Owner-child relationships have inverse will and did
|
||||
['Switcher', 'Box'],
|
||||
['Box', 'Switcher'],
|
||||
);
|
||||
|
||||
testUpdates(
|
||||
[root.refs.child, root.refs.switcher.refs.box],
|
||||
[root.childRef.current, root.switcherRef.current.boxRef.current],
|
||||
// Not owner-child so reconcile independently
|
||||
['Box', 'Child'],
|
||||
['Box', 'Child'],
|
||||
);
|
||||
|
||||
testUpdates(
|
||||
[root.refs.child, root.refs.switcher],
|
||||
[root.childRef.current, root.switcherRef.current],
|
||||
// Switcher owns Box and Child, Box does not own Child
|
||||
['Switcher', 'Box', 'Child'],
|
||||
['Box', 'Switcher', 'Child'],
|
||||
@@ -588,12 +601,13 @@ describe('ReactUpdates', () => {
|
||||
|
||||
class Outer extends React.Component {
|
||||
state = {x: 0};
|
||||
innerRef = React.createRef();
|
||||
|
||||
render() {
|
||||
updates.push('Outer-render-' + this.state.x);
|
||||
return (
|
||||
<div>
|
||||
<Inner x={this.state.x} ref="inner" />
|
||||
<Inner x={this.state.x} ref={this.innerRef} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -602,7 +616,7 @@ describe('ReactUpdates', () => {
|
||||
const x = this.state.x;
|
||||
updates.push('Outer-didUpdate-' + x);
|
||||
updates.push('Inner-setState-' + x);
|
||||
this.refs.inner.setState({x: x}, function() {
|
||||
this.innerRef.current.setState({x: x}, function() {
|
||||
updates.push('Inner-callback-' + x);
|
||||
});
|
||||
}
|
||||
@@ -945,12 +959,14 @@ describe('ReactUpdates', () => {
|
||||
|
||||
it('does not update one component twice in a batch (#2410)', () => {
|
||||
class Parent extends React.Component {
|
||||
childRef = React.createRef();
|
||||
|
||||
getChild = () => {
|
||||
return this.refs.child;
|
||||
return this.childRef.current;
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Child ref="child" />;
|
||||
return <Child ref={this.childRef} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ describe('refs-destruction', () => {
|
||||
}
|
||||
|
||||
TestComponent = class extends React.Component {
|
||||
theInnerDivRef = React.createRef();
|
||||
theInnerClassComponentRef = React.createRef();
|
||||
|
||||
render() {
|
||||
if (this.props.destroy) {
|
||||
return <div />;
|
||||
@@ -43,8 +46,8 @@ describe('refs-destruction', () => {
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<div ref="theInnerDiv" />
|
||||
<ClassComponent ref="theInnerClassComponent" />
|
||||
<div ref={this.theInnerDivRef} />
|
||||
<ClassComponent ref={this.theInnerClassComponentRef} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -55,52 +58,45 @@ describe('refs-destruction', () => {
|
||||
it('should remove refs when destroying the parent', () => {
|
||||
const container = document.createElement('div');
|
||||
const testInstance = ReactDOM.render(<TestComponent />, container);
|
||||
expect(ReactTestUtils.isDOMComponent(testInstance.refs.theInnerDiv)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
expect(
|
||||
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
|
||||
.length,
|
||||
).toEqual(2);
|
||||
ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
|
||||
).toBe(true);
|
||||
expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
|
||||
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
expect(
|
||||
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
|
||||
.length,
|
||||
).toEqual(0);
|
||||
|
||||
expect(testInstance.theInnerDivRef.current).toBe(null);
|
||||
expect(testInstance.theInnerClassComponentRef.current).toBe(null);
|
||||
});
|
||||
|
||||
it('should remove refs when destroying the child', () => {
|
||||
const container = document.createElement('div');
|
||||
const testInstance = ReactDOM.render(<TestComponent />, container);
|
||||
expect(ReactTestUtils.isDOMComponent(testInstance.refs.theInnerDiv)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
|
||||
.length,
|
||||
).toEqual(2);
|
||||
ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
|
||||
).toBe(true);
|
||||
expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
|
||||
|
||||
ReactDOM.render(<TestComponent destroy={true} />, container);
|
||||
expect(
|
||||
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
|
||||
.length,
|
||||
).toEqual(0);
|
||||
|
||||
expect(testInstance.theInnerDivRef.current).toBe(null);
|
||||
expect(testInstance.theInnerClassComponentRef.current).toBe(null);
|
||||
});
|
||||
|
||||
it('should remove refs when removing the child ref attribute', () => {
|
||||
const container = document.createElement('div');
|
||||
const testInstance = ReactDOM.render(<TestComponent />, container);
|
||||
expect(ReactTestUtils.isDOMComponent(testInstance.refs.theInnerDiv)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
expect(
|
||||
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
|
||||
.length,
|
||||
).toEqual(2);
|
||||
ReactTestUtils.isDOMComponent(testInstance.theInnerDivRef.current),
|
||||
).toBe(true);
|
||||
expect(testInstance.theInnerClassComponentRef.current).toBeTruthy();
|
||||
|
||||
ReactDOM.render(<TestComponent removeRef={true} />, container);
|
||||
expect(
|
||||
Object.keys(testInstance.refs || {}).filter(key => testInstance.refs[key])
|
||||
.length,
|
||||
).toEqual(0);
|
||||
|
||||
expect(testInstance.theInnerDivRef.current).toBe(null);
|
||||
expect(testInstance.theInnerClassComponentRef.current).toBe(null);
|
||||
});
|
||||
|
||||
it('should not error when destroying child with ref asynchronously', () => {
|
||||
@@ -135,7 +131,7 @@ describe('refs-destruction', () => {
|
||||
render() {
|
||||
return (
|
||||
<Modal>
|
||||
<a ref="ref" />
|
||||
<a ref={React.createRef()} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
282
packages/react-dom/src/__tests__/refs-test.js
vendored
282
packages/react-dom/src/__tests__/refs-test.js
vendored
@@ -11,86 +11,12 @@
|
||||
|
||||
let React = require('react');
|
||||
let ReactDOM = require('react-dom');
|
||||
let ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
let ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
/**
|
||||
* Counts clicks and has a renders an item for each click. Each item rendered
|
||||
* has a ref of the form "clickLogN".
|
||||
*/
|
||||
class ClickCounter extends React.Component {
|
||||
state = {count: this.props.initialCount};
|
||||
|
||||
triggerReset = () => {
|
||||
this.setState({count: this.props.initialCount});
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
this.setState({count: this.state.count + 1});
|
||||
};
|
||||
|
||||
render() {
|
||||
const children = [];
|
||||
let i;
|
||||
for (i = 0; i < this.state.count; i++) {
|
||||
children.push(
|
||||
<div
|
||||
className="clickLogDiv"
|
||||
key={'clickLog' + i}
|
||||
ref={'clickLog' + i}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="clickIncrementer" onClick={this.handleClick}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only purpose is to test that refs are tracked even when applied to a
|
||||
* component that is injected down several layers. Ref systems are difficult to
|
||||
* build in such a way that ownership is maintained in an airtight manner.
|
||||
*/
|
||||
class GeneralContainerComponent extends React.Component {
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notice how refs ownership is maintained even when injecting a component
|
||||
* into a different parent.
|
||||
*/
|
||||
class TestRefsComponent extends React.Component {
|
||||
doReset = () => {
|
||||
this.refs.myCounter.triggerReset();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div ref="resetDiv" onClick={this.doReset}>
|
||||
Reset Me By Clicking This.
|
||||
</div>
|
||||
<GeneralContainerComponent ref="myContainer">
|
||||
<ClickCounter ref="myCounter" initialCount={1} />
|
||||
</GeneralContainerComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const expectClickLogsLengthToBe = function(instance, length) {
|
||||
const clickLogs = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
||||
instance,
|
||||
'clickLogDiv',
|
||||
);
|
||||
expect(clickLogs.length).toBe(length);
|
||||
expect(Object.keys(instance.refs.myCounter.refs).length).toBe(length);
|
||||
};
|
||||
|
||||
// This is testing if string refs are deleted from `instance.refs`
|
||||
// Once support for string refs is removed, this test can be removed.
|
||||
// Detaching is already tested in refs-detruction-test.js
|
||||
describe('reactiverefs', () => {
|
||||
let container;
|
||||
|
||||
@@ -98,6 +24,7 @@ describe('reactiverefs', () => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
});
|
||||
|
||||
@@ -108,13 +35,117 @@ describe('reactiverefs', () => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Counts clicks and has a renders an item for each click. Each item rendered
|
||||
* has a ref of the form "clickLogN".
|
||||
*/
|
||||
class ClickCounter extends React.Component {
|
||||
state = {count: this.props.initialCount};
|
||||
|
||||
triggerReset = () => {
|
||||
this.setState({count: this.props.initialCount});
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
this.setState({count: this.state.count + 1});
|
||||
};
|
||||
|
||||
render() {
|
||||
const children = [];
|
||||
let i;
|
||||
for (i = 0; i < this.state.count; i++) {
|
||||
children.push(
|
||||
<div
|
||||
className="clickLogDiv"
|
||||
key={'clickLog' + i}
|
||||
ref={'clickLog' + i}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="clickIncrementer" onClick={this.handleClick}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const expectClickLogsLengthToBe = function(instance, length) {
|
||||
const clickLogs = ReactTestUtils.scryRenderedDOMComponentsWithClass(
|
||||
instance,
|
||||
'clickLogDiv',
|
||||
);
|
||||
expect(clickLogs.length).toBe(length);
|
||||
expect(Object.keys(instance.refs.myCounter.refs).length).toBe(length);
|
||||
};
|
||||
|
||||
/**
|
||||
* Render a TestRefsComponent and ensure that the main refs are wired up.
|
||||
*/
|
||||
const renderTestRefsComponent = function() {
|
||||
/**
|
||||
* Only purpose is to test that refs are tracked even when applied to a
|
||||
* component that is injected down several layers. Ref systems are difficult to
|
||||
* build in such a way that ownership is maintained in an airtight manner.
|
||||
*/
|
||||
class GeneralContainerComponent extends React.Component {
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notice how refs ownership is maintained even when injecting a component
|
||||
* into a different parent.
|
||||
*/
|
||||
class TestRefsComponent extends React.Component {
|
||||
doReset = () => {
|
||||
this.refs.myCounter.triggerReset();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div ref="resetDiv" onClick={this.doReset}>
|
||||
Reset Me By Clicking This.
|
||||
</div>
|
||||
<GeneralContainerComponent ref="myContainer">
|
||||
<ClickCounter ref="myCounter" initialCount={1} />
|
||||
</GeneralContainerComponent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
const testRefsComponent = ReactDOM.render(<TestRefsComponent />, container);
|
||||
|
||||
let testRefsComponent;
|
||||
expect(() => {
|
||||
testRefsComponent = ReactDOM.render(<TestRefsComponent />, container);
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "div" contains the string ref "resetDiv". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in div (at **)\n' +
|
||||
' in TestRefsComponent (at **)',
|
||||
'Warning: Component "span" contains the string ref "clickLog0". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in span (at **)\n' +
|
||||
' in ClickCounter (at **)\n' +
|
||||
' in div (at **)\n' +
|
||||
' in GeneralContainerComponent (at **)\n' +
|
||||
' in div (at **)\n' +
|
||||
' in TestRefsComponent (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
|
||||
expect(testRefsComponent instanceof TestRefsComponent).toBe(true);
|
||||
|
||||
const generalContainer = testRefsComponent.refs.myContainer;
|
||||
@@ -156,13 +187,14 @@ describe('reactiverefs', () => {
|
||||
});
|
||||
});
|
||||
|
||||
if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
|
||||
if (!ReactFeatureFlags.disableModulePatternComponents) {
|
||||
describe('factory components', () => {
|
||||
it('Should correctly get the ref', () => {
|
||||
function Comp() {
|
||||
return {
|
||||
elemRef: React.createRef(),
|
||||
render() {
|
||||
return <div ref="elemRef" />;
|
||||
return <div ref={this.elemRef} />;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -177,7 +209,7 @@ if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
|
||||
'`Comp.prototype = React.Component.prototype`. ' +
|
||||
"Don't use an arrow function since it cannot be called with `new` by React.",
|
||||
);
|
||||
expect(inst.refs.elemRef.tagName).toBe('DIV');
|
||||
expect(inst.elemRef.current.tagName).toBe('DIV');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -191,10 +223,15 @@ describe('ref swapping', () => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
|
||||
RefHopsAround = class extends React.Component {
|
||||
state = {count: 0};
|
||||
hopRef = React.createRef();
|
||||
divOneRef = React.createRef();
|
||||
divTwoRef = React.createRef();
|
||||
divThreeRef = React.createRef();
|
||||
|
||||
moveRef = () => {
|
||||
this.setState({count: this.state.count + 1});
|
||||
@@ -212,15 +249,15 @@ describe('ref swapping', () => {
|
||||
<div>
|
||||
<div
|
||||
className="first"
|
||||
ref={count % 3 === 0 ? 'hopRef' : 'divOneRef'}
|
||||
ref={count % 3 === 0 ? this.hopRef : this.divOneRef}
|
||||
/>
|
||||
<div
|
||||
className="second"
|
||||
ref={count % 3 === 1 ? 'hopRef' : 'divTwoRef'}
|
||||
ref={count % 3 === 1 ? this.hopRef : this.divTwoRef}
|
||||
/>
|
||||
<div
|
||||
className="third"
|
||||
ref={count % 3 === 2 ? 'hopRef' : 'divThreeRef'}
|
||||
ref={count % 3 === 2 ? this.hopRef : this.divThreeRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -244,28 +281,28 @@ describe('ref swapping', () => {
|
||||
'third',
|
||||
);
|
||||
|
||||
expect(refHopsAround.refs.hopRef).toEqual(firstDiv);
|
||||
expect(refHopsAround.refs.divTwoRef).toEqual(secondDiv);
|
||||
expect(refHopsAround.refs.divThreeRef).toEqual(thirdDiv);
|
||||
expect(refHopsAround.hopRef.current).toEqual(firstDiv);
|
||||
expect(refHopsAround.divTwoRef.current).toEqual(secondDiv);
|
||||
expect(refHopsAround.divThreeRef.current).toEqual(thirdDiv);
|
||||
|
||||
refHopsAround.moveRef();
|
||||
expect(refHopsAround.refs.divOneRef).toEqual(firstDiv);
|
||||
expect(refHopsAround.refs.hopRef).toEqual(secondDiv);
|
||||
expect(refHopsAround.refs.divThreeRef).toEqual(thirdDiv);
|
||||
expect(refHopsAround.divOneRef.current).toEqual(firstDiv);
|
||||
expect(refHopsAround.hopRef.current).toEqual(secondDiv);
|
||||
expect(refHopsAround.divThreeRef.current).toEqual(thirdDiv);
|
||||
|
||||
refHopsAround.moveRef();
|
||||
expect(refHopsAround.refs.divOneRef).toEqual(firstDiv);
|
||||
expect(refHopsAround.refs.divTwoRef).toEqual(secondDiv);
|
||||
expect(refHopsAround.refs.hopRef).toEqual(thirdDiv);
|
||||
expect(refHopsAround.divOneRef.current).toEqual(firstDiv);
|
||||
expect(refHopsAround.divTwoRef.current).toEqual(secondDiv);
|
||||
expect(refHopsAround.hopRef.current).toEqual(thirdDiv);
|
||||
|
||||
/**
|
||||
* Make sure that after the third, we're back to where we started and the
|
||||
* refs are completely restored.
|
||||
*/
|
||||
refHopsAround.moveRef();
|
||||
expect(refHopsAround.refs.hopRef).toEqual(firstDiv);
|
||||
expect(refHopsAround.refs.divTwoRef).toEqual(secondDiv);
|
||||
expect(refHopsAround.refs.divThreeRef).toEqual(thirdDiv);
|
||||
expect(refHopsAround.hopRef.current).toEqual(firstDiv);
|
||||
expect(refHopsAround.divTwoRef.current).toEqual(secondDiv);
|
||||
expect(refHopsAround.divThreeRef.current).toEqual(thirdDiv);
|
||||
});
|
||||
|
||||
it('always has a value for this.refs', () => {
|
||||
@@ -309,7 +346,20 @@ describe('ref swapping', () => {
|
||||
return <div ref={1} />;
|
||||
}
|
||||
}
|
||||
const a = ReactTestUtils.renderIntoDocument(<A />);
|
||||
let a;
|
||||
expect(() => {
|
||||
a = ReactTestUtils.renderIntoDocument(<A />);
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "A" contains the string ref "1". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in A (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
expect(a.refs[1].nodeName).toBe('DIV');
|
||||
});
|
||||
|
||||
@@ -464,7 +514,7 @@ describe('root level refs', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('creating element with ref in constructor', () => {
|
||||
describe('creating element with string ref in constructor', () => {
|
||||
class RefTest extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -521,13 +571,41 @@ describe('strings refs across renderers', () => {
|
||||
|
||||
const div1 = document.createElement('div');
|
||||
const div2 = document.createElement('div');
|
||||
const inst = ReactDOM.render(<Parent />, div1);
|
||||
|
||||
let inst;
|
||||
expect(() => {
|
||||
inst = ReactDOM.render(<Parent />, div1);
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "Indirection" contains the string ref "child1". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Indirection (at **)\n' +
|
||||
' in Parent (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
|
||||
// Only the first ref has rendered yet.
|
||||
expect(inst.refs.child1.tagName).toBe('DIV');
|
||||
expect(inst.refs.child1).toBe(div1.firstChild);
|
||||
|
||||
// Now both refs should be rendered.
|
||||
ReactDOM.render(<Parent />, div1);
|
||||
expect(() => {
|
||||
// Now both refs should be rendered.
|
||||
ReactDOM.render(<Parent />, div1);
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "Root" contains the string ref "child2". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref',
|
||||
]
|
||||
: [],
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(inst.refs.child1.tagName).toBe('DIV');
|
||||
expect(inst.refs.child1).toBe(div1.firstChild);
|
||||
expect(inst.refs.child2.tagName).toBe('DIV');
|
||||
|
||||
24
packages/react-dom/src/client/ReactDOMLegacy.js
vendored
24
packages/react-dom/src/client/ReactDOMLegacy.js
vendored
@@ -238,10 +238,22 @@ function legacyRenderSubtreeIntoContainer(
|
||||
return getPublicRootInstance(root);
|
||||
}
|
||||
|
||||
let didWarnAboutFindDOMNode = false;
|
||||
|
||||
export function findDOMNode(
|
||||
componentOrElement: Element | ?React$Component<any, any>,
|
||||
): null | Element | Text {
|
||||
if (__DEV__) {
|
||||
if (!didWarnAboutFindDOMNode) {
|
||||
didWarnAboutFindDOMNode = true;
|
||||
console.error(
|
||||
'findDOMNode is deprecated and will be removed in the next major ' +
|
||||
'release. Instead, add a ref directly to the element you want ' +
|
||||
'to reference. Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-find-node',
|
||||
);
|
||||
}
|
||||
|
||||
const owner = (ReactCurrentOwner.current: any);
|
||||
if (owner !== null && owner.stateNode !== null) {
|
||||
const warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
|
||||
@@ -381,7 +393,19 @@ export function unstable_renderSubtreeIntoContainer(
|
||||
);
|
||||
}
|
||||
|
||||
let didWarnAboutUnmountComponentAtNode = false;
|
||||
export function unmountComponentAtNode(container: Container) {
|
||||
if (__DEV__) {
|
||||
if (!didWarnAboutUnmountComponentAtNode) {
|
||||
didWarnAboutUnmountComponentAtNode = true;
|
||||
console.error(
|
||||
'unmountComponentAtNode is deprecated and will be removed in the ' +
|
||||
'next major release. Switch to the createRoot API. Learn ' +
|
||||
'more: https://reactjs.org/link/switch-to-createroot',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidContainerLegacy(container)) {
|
||||
throw new Error(
|
||||
'unmountComponentAtNode(...): Target container is not a DOM element.',
|
||||
|
||||
@@ -100,6 +100,13 @@ function renderToStaticNodeStream(
|
||||
children: ReactNodeList,
|
||||
options?: ServerOptions,
|
||||
): Readable {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'ReactDOMServer.renderToStaticNodeStream() is deprecated.' +
|
||||
' Use ReactDOMServer.renderToPipeableStream() and wait to `pipe` until the `onAllReady`' +
|
||||
' callback has been called instead.',
|
||||
);
|
||||
}
|
||||
return renderToNodeStreamImpl(children, options, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ const getFiberCurrentPropsFromNode = EventInternals[2];
|
||||
const enqueueStateRestore = EventInternals[3];
|
||||
const restoreStateIfNeeded = EventInternals[4];
|
||||
|
||||
const act = React.unstable_act;
|
||||
const reactAct = React.unstable_act;
|
||||
|
||||
function Event(suffix) {}
|
||||
|
||||
@@ -121,7 +121,23 @@ function validateClassInstance(inst, methodName) {
|
||||
* utilities will suffice for testing purposes.
|
||||
* @lends ReactTestUtils
|
||||
*/
|
||||
|
||||
let didWarnAboutReactTestUtilsDeprecation = false;
|
||||
|
||||
function renderIntoDocument(element) {
|
||||
if (__DEV__) {
|
||||
if (!didWarnAboutReactTestUtilsDeprecation) {
|
||||
didWarnAboutReactTestUtilsDeprecation = true;
|
||||
console.error(
|
||||
'ReactDOMTestUtils is deprecated and will be removed in a future ' +
|
||||
'major release, because it exposes internal implementation details ' +
|
||||
'that are highly likely to change between releases. Upgrade to a ' +
|
||||
'modern testing library, such as @testing-library/react. See ' +
|
||||
'https://react.dev/warnings/react-dom-test-utils for more info.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
// None of our tests actually require attaching the container to the
|
||||
// DOM, and doing so creates a mess that we rely on test isolation to
|
||||
@@ -711,6 +727,23 @@ function buildSimulators() {
|
||||
}
|
||||
buildSimulators();
|
||||
|
||||
let didWarnAboutUsingAct = false;
|
||||
export const act = __DEV__
|
||||
? function actWithWarning(callback) {
|
||||
if (__DEV__) {
|
||||
if (!didWarnAboutUsingAct) {
|
||||
didWarnAboutUsingAct = true;
|
||||
console.error(
|
||||
'`ReactDOMTestUtils.act` is deprecated in favor of `React.act`. ' +
|
||||
'Import `act` from `react` instead of `react-dom/test-utils`. ' +
|
||||
'See https://react.dev/warnings/react-dom-test-utils for more info.',
|
||||
);
|
||||
}
|
||||
}
|
||||
return reactAct(callback);
|
||||
}
|
||||
: reactAct;
|
||||
|
||||
export {
|
||||
renderIntoDocument,
|
||||
isElement,
|
||||
@@ -729,5 +762,4 @@ export {
|
||||
mockComponent,
|
||||
nativeTouchData,
|
||||
Simulate,
|
||||
act,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-is",
|
||||
"version": "18.1.0",
|
||||
"version": "18.3.0",
|
||||
"description": "Brand checking of React Elements.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -1385,25 +1385,36 @@ describe('ResponderEventPlugin', () => {
|
||||
const ReactDOMComponentTree = require('react-dom/src/client/ReactDOMComponentTree');
|
||||
|
||||
class ChildComponent extends React.Component {
|
||||
divRef = React.createRef();
|
||||
div1Ref = React.createRef();
|
||||
div2Ref = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref="DIV" id={this.props.id + '__DIV'}>
|
||||
<div ref="DIV_1" id={this.props.id + '__DIV_1'} />
|
||||
<div ref="DIV_2" id={this.props.id + '__DIV_2'} />
|
||||
<div ref={this.divRef} id={this.props.id + '__DIV'}>
|
||||
<div ref={this.div1Ref} id={this.props.id + '__DIV_1'} />
|
||||
<div ref={this.div2Ref} id={this.props.id + '__DIV_2'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ParentComponent extends React.Component {
|
||||
pRef = React.createRef();
|
||||
p_P1Ref = React.createRef();
|
||||
p_P1Ref = React.createRef();
|
||||
p_P1_C1Ref = React.createRef();
|
||||
p_P1_C2Ref = React.createRef();
|
||||
p_OneOffRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref="P" id="P">
|
||||
<div ref="P_P1" id="P_P1">
|
||||
<ChildComponent ref="P_P1_C1" id="P_P1_C1" />
|
||||
<ChildComponent ref="P_P1_C2" id="P_P1_C2" />
|
||||
<div ref={this.pRef} id="P">
|
||||
<div ref={this.p_P1Ref} id="P_P1">
|
||||
<ChildComponent ref={this.p_P1_C1Ref} id="P_P1_C1" />
|
||||
<ChildComponent ref={this.p_P1_C2Ref} id="P_P1_C2" />
|
||||
</div>
|
||||
<div ref="P_OneOff" id="P_OneOff" />
|
||||
<div ref={this.p_OneOffRef} id="P_OneOff" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1414,41 +1425,45 @@ describe('ResponderEventPlugin', () => {
|
||||
const ancestors = [
|
||||
// Common ancestor with self is self.
|
||||
{
|
||||
one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
com: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
one: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
two: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
com: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
},
|
||||
// Common ancestor with self is self - even if topmost DOM.
|
||||
{one: parent.refs.P, two: parent.refs.P, com: parent.refs.P},
|
||||
{
|
||||
one: parent.pRef.current,
|
||||
two: parent.pRef.current,
|
||||
com: parent.pRef.current,
|
||||
},
|
||||
// Siblings
|
||||
{
|
||||
one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: parent.refs.P_P1_C1.refs.DIV_2,
|
||||
com: parent.refs.P_P1_C1.refs.DIV,
|
||||
one: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
two: parent.p_P1_C1Ref.current.div2Ref.current,
|
||||
com: parent.p_P1_C1Ref.current.divRef.current,
|
||||
},
|
||||
// Common ancestor with parent is the parent.
|
||||
{
|
||||
one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: parent.refs.P_P1_C1.refs.DIV,
|
||||
com: parent.refs.P_P1_C1.refs.DIV,
|
||||
one: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
two: parent.p_P1_C1Ref.current.divRef.current,
|
||||
com: parent.p_P1_C1Ref.current.divRef.current,
|
||||
},
|
||||
// Common ancestor with grandparent is the grandparent.
|
||||
{
|
||||
one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: parent.refs.P_P1,
|
||||
com: parent.refs.P_P1,
|
||||
one: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
two: parent.p_P1Ref.current,
|
||||
com: parent.p_P1Ref.current,
|
||||
},
|
||||
// Grandparent across subcomponent boundaries.
|
||||
{
|
||||
one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: parent.refs.P_P1_C2.refs.DIV_1,
|
||||
com: parent.refs.P_P1,
|
||||
one: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
two: parent.p_P1_C2Ref.current.div1Ref.current,
|
||||
com: parent.p_P1Ref.current,
|
||||
},
|
||||
// Something deep with something one-off.
|
||||
{
|
||||
one: parent.refs.P_P1_C1.refs.DIV_1,
|
||||
two: parent.refs.P_OneOff,
|
||||
com: parent.refs.P,
|
||||
one: parent.p_P1_C1Ref.current.div1Ref.current,
|
||||
two: parent.p_OneOffRef.current,
|
||||
com: parent.pRef.current,
|
||||
},
|
||||
];
|
||||
let i;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.1.0"
|
||||
"react": "^18.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
createFiberFromText,
|
||||
createFiberFromPortal,
|
||||
} from './ReactFiber.new';
|
||||
import {emptyRefsObject} from './ReactFiberClassComponent.new';
|
||||
import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading.new';
|
||||
import {StrictLegacyMode} from './ReactTypeOfMode';
|
||||
import {getIsHydrating} from './ReactFiberHydrationContext.new';
|
||||
@@ -92,6 +91,10 @@ if (__DEV__) {
|
||||
};
|
||||
}
|
||||
|
||||
function isReactClass(type) {
|
||||
return type.prototype && type.prototype.isReactComponent;
|
||||
}
|
||||
|
||||
function coerceRef(
|
||||
returnFiber: Fiber,
|
||||
current: Fiber | null,
|
||||
@@ -115,7 +118,16 @@ function coerceRef(
|
||||
element._owner &&
|
||||
element._self &&
|
||||
element._owner.stateNode !== element._self
|
||||
)
|
||||
) &&
|
||||
// Will already throw with "Function components cannot have string refs"
|
||||
!(
|
||||
element._owner &&
|
||||
((element._owner: any): Fiber).tag !== ClassComponent
|
||||
) &&
|
||||
// Will already warn with "Function components cannot be given refs"
|
||||
!(typeof element.type === 'function' && !isReactClass(element.type)) &&
|
||||
// Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
|
||||
element._owner
|
||||
) {
|
||||
const componentName =
|
||||
getComponentNameFromFiber(returnFiber) || 'Component';
|
||||
@@ -186,11 +198,7 @@ function coerceRef(
|
||||
return current.ref;
|
||||
}
|
||||
const ref = function(value) {
|
||||
let refs = resolvedInst.refs;
|
||||
if (refs === emptyRefsObject) {
|
||||
// This is a lazy pooled frozen object, so we need to initialize.
|
||||
refs = resolvedInst.refs = {};
|
||||
}
|
||||
const refs = resolvedInst.refs;
|
||||
if (value === null) {
|
||||
delete refs[stringRef];
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
createFiberFromText,
|
||||
createFiberFromPortal,
|
||||
} from './ReactFiber.old';
|
||||
import {emptyRefsObject} from './ReactFiberClassComponent.old';
|
||||
import {isCompatibleFamilyForHotReloading} from './ReactFiberHotReloading.old';
|
||||
import {StrictLegacyMode} from './ReactTypeOfMode';
|
||||
import {getIsHydrating} from './ReactFiberHydrationContext.old';
|
||||
@@ -92,6 +91,10 @@ if (__DEV__) {
|
||||
};
|
||||
}
|
||||
|
||||
function isReactClass(type) {
|
||||
return type.prototype && type.prototype.isReactComponent;
|
||||
}
|
||||
|
||||
function coerceRef(
|
||||
returnFiber: Fiber,
|
||||
current: Fiber | null,
|
||||
@@ -115,7 +118,16 @@ function coerceRef(
|
||||
element._owner &&
|
||||
element._self &&
|
||||
element._owner.stateNode !== element._self
|
||||
)
|
||||
) &&
|
||||
// Will already throw with "Function components cannot have string refs"
|
||||
!(
|
||||
element._owner &&
|
||||
((element._owner: any): Fiber).tag !== ClassComponent
|
||||
) &&
|
||||
// Will already warn with "Function components cannot be given refs"
|
||||
!(typeof element.type === 'function' && !isReactClass(element.type)) &&
|
||||
// Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
|
||||
element._owner
|
||||
) {
|
||||
const componentName =
|
||||
getComponentNameFromFiber(returnFiber) || 'Component';
|
||||
@@ -186,11 +198,7 @@ function coerceRef(
|
||||
return current.ref;
|
||||
}
|
||||
const ref = function(value) {
|
||||
let refs = resolvedInst.refs;
|
||||
if (refs === emptyRefsObject) {
|
||||
// This is a lazy pooled frozen object, so we need to initialize.
|
||||
refs = resolvedInst.refs = {};
|
||||
}
|
||||
const refs = resolvedInst.refs;
|
||||
if (value === null) {
|
||||
delete refs[stringRef];
|
||||
} else {
|
||||
|
||||
@@ -496,6 +496,20 @@ function updateMemoComponent(
|
||||
getComponentNameFromType(type),
|
||||
);
|
||||
}
|
||||
if (
|
||||
warnAboutDefaultPropsOnFunctionComponents &&
|
||||
Component.defaultProps !== undefined
|
||||
) {
|
||||
const componentName = getComponentNameFromType(type) || 'Unknown';
|
||||
if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
|
||||
console.error(
|
||||
'%s: Support for defaultProps will be removed from memo components ' +
|
||||
'in a future major release. Use JavaScript default parameters instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
const child = createFiberFromTypeAndProps(
|
||||
Component.type,
|
||||
|
||||
@@ -496,6 +496,20 @@ function updateMemoComponent(
|
||||
getComponentNameFromType(type),
|
||||
);
|
||||
}
|
||||
if (
|
||||
warnAboutDefaultPropsOnFunctionComponents &&
|
||||
Component.defaultProps !== undefined
|
||||
) {
|
||||
const componentName = getComponentNameFromType(type) || 'Unknown';
|
||||
if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
|
||||
console.error(
|
||||
'%s: Support for defaultProps will be removed from memo components ' +
|
||||
'in a future major release. Use JavaScript default parameters instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
const child = createFiberFromTypeAndProps(
|
||||
Component.type,
|
||||
|
||||
@@ -12,7 +12,6 @@ import type {Lanes} from './ReactFiberLane.new';
|
||||
import type {UpdateQueue} from './ReactFiberClassUpdateQueue.new';
|
||||
import type {Flags} from './ReactFiberFlags';
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
LayoutStatic,
|
||||
MountLayoutDev,
|
||||
@@ -82,10 +81,6 @@ import {
|
||||
|
||||
const fakeInternalInstance = {};
|
||||
|
||||
// React.Component uses a shared frozen object by default.
|
||||
// We'll use it to determine whether we need to initialize legacy refs.
|
||||
export const emptyRefsObject = new React.Component().refs;
|
||||
|
||||
let didWarnAboutStateAssignmentForComponent;
|
||||
let didWarnAboutUninitializedState;
|
||||
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
|
||||
@@ -96,6 +91,7 @@ let warnOnInvalidCallback;
|
||||
let didWarnAboutDirectlyAssigningPropsToState;
|
||||
let didWarnAboutContextTypeAndContextTypes;
|
||||
let didWarnAboutInvalidateContextType;
|
||||
let didWarnAboutLegacyContext;
|
||||
|
||||
if (__DEV__) {
|
||||
didWarnAboutStateAssignmentForComponent = new Set();
|
||||
@@ -106,6 +102,7 @@ if (__DEV__) {
|
||||
didWarnAboutUndefinedDerivedState = new Set();
|
||||
didWarnAboutContextTypeAndContextTypes = new Set();
|
||||
didWarnAboutInvalidateContextType = new Set();
|
||||
didWarnAboutLegacyContext = new Set();
|
||||
|
||||
const didWarnOnInvalidCallback = new Set();
|
||||
|
||||
@@ -435,6 +432,39 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
ctor.childContextTypes &&
|
||||
!didWarnAboutLegacyContext.has(ctor) &&
|
||||
// Strict Mode has its own warning for legacy context, so we can skip
|
||||
// this one.
|
||||
(workInProgress.mode & StrictLegacyMode) === NoMode
|
||||
) {
|
||||
didWarnAboutLegacyContext.add(ctor);
|
||||
console.error(
|
||||
'%s uses the legacy childContextTypes API which is no longer ' +
|
||||
'supported and will be removed in the next major release. Use ' +
|
||||
'React.createContext() instead\n\n.' +
|
||||
'Learn more about this warning here: https://reactjs.org/link/legacy-context',
|
||||
name,
|
||||
);
|
||||
}
|
||||
if (
|
||||
ctor.contextTypes &&
|
||||
!didWarnAboutLegacyContext.has(ctor) &&
|
||||
// Strict Mode has its own warning for legacy context, so we can skip
|
||||
// this one.
|
||||
(workInProgress.mode & StrictLegacyMode) === NoMode
|
||||
) {
|
||||
didWarnAboutLegacyContext.add(ctor);
|
||||
console.error(
|
||||
'%s uses the legacy contextTypes API which is no longer supported ' +
|
||||
'and will be removed in the next major release. Use ' +
|
||||
'React.createContext() with static contextType instead.\n\n' +
|
||||
'Learn more about this warning here: https://reactjs.org/link/legacy-context',
|
||||
name,
|
||||
);
|
||||
}
|
||||
|
||||
if (instance.contextTypes) {
|
||||
console.error(
|
||||
'contextTypes was defined as an instance property on %s. Use a static ' +
|
||||
@@ -836,7 +866,7 @@ function mountClassInstance(
|
||||
const instance = workInProgress.stateNode;
|
||||
instance.props = newProps;
|
||||
instance.state = workInProgress.memoizedState;
|
||||
instance.refs = emptyRefsObject;
|
||||
instance.refs = {};
|
||||
|
||||
initializeUpdateQueue(workInProgress);
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import type {Lanes} from './ReactFiberLane.old';
|
||||
import type {UpdateQueue} from './ReactFiberClassUpdateQueue.old';
|
||||
import type {Flags} from './ReactFiberFlags';
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
LayoutStatic,
|
||||
MountLayoutDev,
|
||||
@@ -82,10 +81,6 @@ import {
|
||||
|
||||
const fakeInternalInstance = {};
|
||||
|
||||
// React.Component uses a shared frozen object by default.
|
||||
// We'll use it to determine whether we need to initialize legacy refs.
|
||||
export const emptyRefsObject = new React.Component().refs;
|
||||
|
||||
let didWarnAboutStateAssignmentForComponent;
|
||||
let didWarnAboutUninitializedState;
|
||||
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
|
||||
@@ -96,6 +91,7 @@ let warnOnInvalidCallback;
|
||||
let didWarnAboutDirectlyAssigningPropsToState;
|
||||
let didWarnAboutContextTypeAndContextTypes;
|
||||
let didWarnAboutInvalidateContextType;
|
||||
let didWarnAboutLegacyContext;
|
||||
|
||||
if (__DEV__) {
|
||||
didWarnAboutStateAssignmentForComponent = new Set();
|
||||
@@ -106,6 +102,7 @@ if (__DEV__) {
|
||||
didWarnAboutUndefinedDerivedState = new Set();
|
||||
didWarnAboutContextTypeAndContextTypes = new Set();
|
||||
didWarnAboutInvalidateContextType = new Set();
|
||||
didWarnAboutLegacyContext = new Set();
|
||||
|
||||
const didWarnOnInvalidCallback = new Set();
|
||||
|
||||
@@ -435,6 +432,39 @@ function checkClassInstance(workInProgress: Fiber, ctor: any, newProps: any) {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
ctor.childContextTypes &&
|
||||
!didWarnAboutLegacyContext.has(ctor) &&
|
||||
// Strict Mode has its own warning for legacy context, so we can skip
|
||||
// this one.
|
||||
(workInProgress.mode & StrictLegacyMode) === NoMode
|
||||
) {
|
||||
didWarnAboutLegacyContext.add(ctor);
|
||||
console.error(
|
||||
'%s uses the legacy childContextTypes API which is no longer ' +
|
||||
'supported and will be removed in the next major release. Use ' +
|
||||
'React.createContext() instead\n\n.' +
|
||||
'Learn more about this warning here: https://reactjs.org/link/legacy-context',
|
||||
name,
|
||||
);
|
||||
}
|
||||
if (
|
||||
ctor.contextTypes &&
|
||||
!didWarnAboutLegacyContext.has(ctor) &&
|
||||
// Strict Mode has its own warning for legacy context, so we can skip
|
||||
// this one.
|
||||
(workInProgress.mode & StrictLegacyMode) === NoMode
|
||||
) {
|
||||
didWarnAboutLegacyContext.add(ctor);
|
||||
console.error(
|
||||
'%s uses the legacy contextTypes API which is no longer supported ' +
|
||||
'and will be removed in the next major release. Use ' +
|
||||
'React.createContext() with static contextType instead.\n\n' +
|
||||
'Learn more about this warning here: https://reactjs.org/link/legacy-context',
|
||||
name,
|
||||
);
|
||||
}
|
||||
|
||||
if (instance.contextTypes) {
|
||||
console.error(
|
||||
'contextTypes was defined as an instance property on %s. Use a static ' +
|
||||
@@ -836,7 +866,7 @@ function mountClassInstance(
|
||||
const instance = workInProgress.stateNode;
|
||||
instance.props = newProps;
|
||||
instance.state = workInProgress.memoizedState;
|
||||
instance.refs = emptyRefsObject;
|
||||
instance.refs = {};
|
||||
|
||||
initializeUpdateQueue(workInProgress);
|
||||
|
||||
|
||||
74
packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js
vendored
Normal file
74
packages/react-reconciler/src/__tests__/ReactFiberRefs-test.js
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and 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 ReactNoop;
|
||||
let act;
|
||||
|
||||
describe('ReactFiberRefs', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
act = require('jest-react').act;
|
||||
});
|
||||
|
||||
test('strings refs can be codemodded to callback refs', async () => {
|
||||
let app;
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
app = this;
|
||||
return (
|
||||
<div
|
||||
prop="Hello!"
|
||||
ref={el => {
|
||||
// `refs` used to be a shared frozen object unless/until a string
|
||||
// ref attached by the reconciler, but it's not anymore so that we
|
||||
// can codemod string refs to userspace callback refs.
|
||||
this.refs.div = el;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
await act(async () => root.render(<App />));
|
||||
expect(app.refs.div.prop).toBe('Hello!');
|
||||
});
|
||||
|
||||
test('class refs are initialized to a frozen shared object', async () => {
|
||||
const refsCollection = new Set();
|
||||
class Component extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
refsCollection.add(this.refs);
|
||||
}
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactNoop.createRoot();
|
||||
await act(() =>
|
||||
root.render(
|
||||
<>
|
||||
<Component />
|
||||
<Component />
|
||||
</>,
|
||||
),
|
||||
);
|
||||
|
||||
expect(refsCollection.size).toBe(1);
|
||||
const refsInstance = Array.from(refsCollection)[0];
|
||||
expect(Object.isFrozen(refsInstance)).toBe(__DEV__);
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,7 @@
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
let ReactNoop;
|
||||
let Scheduler;
|
||||
|
||||
@@ -19,6 +20,7 @@ describe('ReactIncrementalSideEffects', () => {
|
||||
jest.resetModules();
|
||||
|
||||
React = require('react');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
Scheduler = require('scheduler');
|
||||
});
|
||||
@@ -1306,8 +1308,19 @@ describe('ReactIncrementalSideEffects', () => {
|
||||
}
|
||||
|
||||
ReactNoop.render(<Foo />);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "Foo" contains the string ref "bar". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Foo (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
expect(fooInstance.refs.bar.test).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -293,7 +293,12 @@ describe('ReactLazy', () => {
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Hi']);
|
||||
expect(() => expect(Scheduler).toFlushAndYield(['Hi'])).toErrorDev(
|
||||
'Warning: T: Support for defaultProps ' +
|
||||
'will be removed from function components in a future major ' +
|
||||
'release. Use JavaScript default parameters instead.',
|
||||
);
|
||||
|
||||
expect(root).toMatchRenderedOutput('Hi');
|
||||
|
||||
T.defaultProps = {text: 'Hi again'};
|
||||
@@ -343,7 +348,14 @@ describe('ReactLazy', () => {
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Lazy', 'Sibling', 'A']);
|
||||
expect(() =>
|
||||
expect(Scheduler).toFlushAndYield(['Lazy', 'Sibling', 'A']),
|
||||
).toErrorDev(
|
||||
'Warning: LazyImpl: Support for defaultProps ' +
|
||||
'will be removed from function components in a future major ' +
|
||||
'release. Use JavaScript default parameters instead.',
|
||||
);
|
||||
|
||||
expect(root).toMatchRenderedOutput('SiblingA');
|
||||
|
||||
// Lazy should not re-render
|
||||
@@ -643,7 +655,12 @@ describe('ReactLazy', () => {
|
||||
expect(root).not.toMatchRenderedOutput('Hi Bye');
|
||||
|
||||
await Promise.resolve();
|
||||
expect(Scheduler).toFlushAndYield(['Hi Bye']);
|
||||
expect(() => expect(Scheduler).toFlushAndYield(['Hi Bye'])).toErrorDev(
|
||||
'Warning: T: Support for defaultProps ' +
|
||||
'will be removed from function components in a future major ' +
|
||||
'release. Use JavaScript default parameters instead.',
|
||||
);
|
||||
|
||||
expect(root).toMatchRenderedOutput('Hi Bye');
|
||||
|
||||
root.update(
|
||||
@@ -732,7 +749,11 @@ describe('ReactLazy', () => {
|
||||
);
|
||||
});
|
||||
|
||||
async function verifyInnerPropTypesAreChecked(Add) {
|
||||
async function verifyInnerPropTypesAreChecked(
|
||||
Add,
|
||||
shouldWarnAboutFunctionDefaultProps,
|
||||
shouldWarnAboutMemoDefaultProps,
|
||||
) {
|
||||
const LazyAdd = lazy(() => fakeImport(Add));
|
||||
expect(() => {
|
||||
LazyAdd.propTypes = {};
|
||||
@@ -753,15 +774,28 @@ describe('ReactLazy', () => {
|
||||
);
|
||||
|
||||
expect(Scheduler).toFlushAndYield(['Loading...']);
|
||||
|
||||
expect(root).not.toMatchRenderedOutput('22');
|
||||
|
||||
// Mount
|
||||
await Promise.resolve();
|
||||
expect(() => {
|
||||
Scheduler.unstable_flushAll();
|
||||
}).toErrorDev([
|
||||
'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
|
||||
]);
|
||||
}).toErrorDev(
|
||||
shouldWarnAboutFunctionDefaultProps
|
||||
? [
|
||||
'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
|
||||
]
|
||||
: shouldWarnAboutMemoDefaultProps
|
||||
? [
|
||||
'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
|
||||
]
|
||||
: [
|
||||
'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
|
||||
],
|
||||
);
|
||||
expect(root).toMatchRenderedOutput('22');
|
||||
|
||||
// Update
|
||||
@@ -792,7 +826,7 @@ describe('ReactLazy', () => {
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyInnerPropTypesAreChecked(Add, true);
|
||||
});
|
||||
|
||||
it('respects propTypes on function component without defaultProps', async () => {
|
||||
@@ -874,7 +908,7 @@ describe('ReactLazy', () => {
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(Add);
|
||||
await verifyInnerPropTypesAreChecked(Add, false, true);
|
||||
});
|
||||
|
||||
it('respects propTypes on outer memo component without defaultProps', async () => {
|
||||
@@ -901,7 +935,7 @@ describe('ReactLazy', () => {
|
||||
Add.defaultProps = {
|
||||
innerWithDefault: 42,
|
||||
};
|
||||
await verifyInnerPropTypesAreChecked(React.memo(Add));
|
||||
await verifyInnerPropTypesAreChecked(React.memo(Add), true);
|
||||
});
|
||||
|
||||
it('respects propTypes on inner memo component without defaultProps', async () => {
|
||||
@@ -944,9 +978,10 @@ describe('ReactLazy', () => {
|
||||
await Promise.resolve();
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushAndYield(['Inner default text']);
|
||||
}).toErrorDev(
|
||||
}).toErrorDev([
|
||||
'T: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
'The prop `text` is marked as required in `T`, but its value is `undefined`',
|
||||
);
|
||||
]);
|
||||
expect(root).toMatchRenderedOutput('Inner default text');
|
||||
|
||||
// Update
|
||||
@@ -1058,7 +1093,11 @@ describe('ReactLazy', () => {
|
||||
|
||||
// Mount
|
||||
await Promise.resolve();
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toErrorDev(
|
||||
'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
);
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
|
||||
// Update (shallowly equal)
|
||||
@@ -1142,7 +1181,12 @@ describe('ReactLazy', () => {
|
||||
|
||||
// Mount
|
||||
await Promise.resolve();
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toErrorDev([
|
||||
'Memo: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
]);
|
||||
expect(root).toMatchRenderedOutput('4');
|
||||
|
||||
// Update
|
||||
|
||||
@@ -75,6 +75,7 @@ describe('memo', () => {
|
||||
}
|
||||
ReactNoop.render(<Outer />);
|
||||
expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([
|
||||
'App: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
|
||||
'Warning: Function components cannot be given refs. Attempts to access ' +
|
||||
'this ref will fail.',
|
||||
]);
|
||||
@@ -441,7 +442,11 @@ describe('memo', () => {
|
||||
);
|
||||
expect(Scheduler).toFlushAndYield(['Loading...']);
|
||||
await Promise.resolve();
|
||||
expect(Scheduler).toFlushAndYield([15]);
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushAndYield([15]);
|
||||
}).toErrorDev([
|
||||
'Counter: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
]);
|
||||
expect(ReactNoop.getChildren()).toEqual([span(15)]);
|
||||
|
||||
// Should bail out because props have not changed
|
||||
@@ -552,7 +557,11 @@ describe('memo', () => {
|
||||
<Outer />
|
||||
</div>,
|
||||
);
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushWithoutYielding();
|
||||
}).toErrorDev([
|
||||
'Inner: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
|
||||
]);
|
||||
|
||||
// Mount
|
||||
expect(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-test-renderer",
|
||||
"version": "18.1.0",
|
||||
"version": "18.3.0",
|
||||
"description": "React package for snapshot testing.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -19,12 +19,12 @@
|
||||
},
|
||||
"homepage": "https://reactjs.org/",
|
||||
"dependencies": {
|
||||
"react-is": "^18.1.0",
|
||||
"react-is": "^18.3.0",
|
||||
"react-shallow-renderer": "^16.15.0",
|
||||
"scheduler": "^0.22.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.1.0"
|
||||
"react": "^18.3.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
||||
@@ -271,13 +271,15 @@ describe('ReactTestRenderer', () => {
|
||||
return <div>Hello, world</div>;
|
||||
}
|
||||
class Foo extends React.Component {
|
||||
fooRef = React.createRef();
|
||||
render() {
|
||||
return <Bar ref="foo" />;
|
||||
return <Bar ref={this.fooRef} />;
|
||||
}
|
||||
}
|
||||
class Baz extends React.Component {
|
||||
bazRef = React.createRef();
|
||||
render() {
|
||||
return <div ref="baz" />;
|
||||
return <div ref={this.bazRef} />;
|
||||
}
|
||||
}
|
||||
ReactTestRenderer.create(<Baz />);
|
||||
@@ -298,11 +300,12 @@ describe('ReactTestRenderer', () => {
|
||||
const mockAnchorInstance = {hover: () => {}};
|
||||
const log = [];
|
||||
class Foo extends React.Component {
|
||||
barRef = React.createRef();
|
||||
componentDidMount() {
|
||||
log.push(this.refs.bar);
|
||||
log.push(this.barRef.current);
|
||||
}
|
||||
render() {
|
||||
return <a ref="bar">Hello, world</a>;
|
||||
return <a ref={this.barRef}>Hello, world</a>;
|
||||
}
|
||||
}
|
||||
function createNodeMock(element) {
|
||||
@@ -355,7 +358,7 @@ describe('ReactTestRenderer', () => {
|
||||
it('supports unmounting when using refs', () => {
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return <div ref="foo" />;
|
||||
return <div ref={React.createRef()} />;
|
||||
}
|
||||
}
|
||||
const inst = ReactTestRenderer.create(<Foo />, {
|
||||
@@ -394,7 +397,11 @@ describe('ReactTestRenderer', () => {
|
||||
};
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return this.props.useDiv ? <div ref="foo" /> : <span ref="foo" />;
|
||||
return this.props.useDiv ? (
|
||||
<div ref={React.createRef()} />
|
||||
) : (
|
||||
<span ref={React.createRef()} />
|
||||
);
|
||||
}
|
||||
}
|
||||
const inst = ReactTestRenderer.create(<Foo useDiv={true} />, {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"keywords": [
|
||||
"react"
|
||||
],
|
||||
"version": "18.1.0",
|
||||
"version": "18.3.0",
|
||||
"homepage": "https://reactjs.org/",
|
||||
"bugs": "https://github.com/facebook/react/issues",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -279,6 +279,8 @@ function validateFragmentProps(fragment) {
|
||||
}
|
||||
}
|
||||
|
||||
const didWarnAboutKeySpread = {};
|
||||
|
||||
export function jsxWithValidation(
|
||||
type,
|
||||
props,
|
||||
@@ -287,115 +289,132 @@ export function jsxWithValidation(
|
||||
source,
|
||||
self,
|
||||
) {
|
||||
const validType = isValidElementType(type);
|
||||
|
||||
// We warn in this case but don't throw. We expect the element creation to
|
||||
// succeed and there will likely be errors in render.
|
||||
if (!validType) {
|
||||
let info = '';
|
||||
if (
|
||||
type === undefined ||
|
||||
(typeof type === 'object' &&
|
||||
type !== null &&
|
||||
Object.keys(type).length === 0)
|
||||
) {
|
||||
info +=
|
||||
' You likely forgot to export your component from the file ' +
|
||||
"it's defined in, or you might have mixed up default and named imports.";
|
||||
}
|
||||
|
||||
const sourceInfo = getSourceInfoErrorAddendum(source);
|
||||
if (sourceInfo) {
|
||||
info += sourceInfo;
|
||||
} else {
|
||||
info += getDeclarationErrorAddendum();
|
||||
}
|
||||
|
||||
let typeString;
|
||||
if (type === null) {
|
||||
typeString = 'null';
|
||||
} else if (isArray(type)) {
|
||||
typeString = 'array';
|
||||
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
|
||||
typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`;
|
||||
info =
|
||||
' Did you accidentally export a JSX literal instead of a component?';
|
||||
} else {
|
||||
typeString = typeof type;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'React.jsx: type is invalid -- expected a string (for ' +
|
||||
'built-in components) or a class/function (for composite ' +
|
||||
'components) but got: %s.%s',
|
||||
typeString,
|
||||
info,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const element = jsxDEV(type, props, key, source, self);
|
||||
|
||||
// The result can be nullish if a mock or a custom function is used.
|
||||
// TODO: Drop this when these are no longer allowed as the type argument.
|
||||
if (element == null) {
|
||||
return element;
|
||||
}
|
||||
|
||||
// Skip key warning if the type isn't valid since our key validation logic
|
||||
// doesn't expect a non-string/function type and can throw confusing errors.
|
||||
// We don't want exception behavior to differ between dev and prod.
|
||||
// (Rendering will throw with a helpful message and as soon as the type is
|
||||
// fixed, the key warnings will appear.)
|
||||
|
||||
if (validType) {
|
||||
const children = props.children;
|
||||
if (children !== undefined) {
|
||||
if (isStaticChildren) {
|
||||
if (isArray(children)) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
validateChildKeys(children[i], type);
|
||||
}
|
||||
|
||||
if (Object.freeze) {
|
||||
Object.freeze(children);
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'React.jsx: Static children should always be an array. ' +
|
||||
'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
|
||||
'Use the Babel transform instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validateChildKeys(children, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
if (warnAboutSpreadingKeyToJSX) {
|
||||
if (hasOwnProperty.call(props, 'key')) {
|
||||
const validType = isValidElementType(type);
|
||||
|
||||
// We warn in this case but don't throw. We expect the element creation to
|
||||
// succeed and there will likely be errors in render.
|
||||
if (!validType) {
|
||||
let info = '';
|
||||
if (
|
||||
type === undefined ||
|
||||
(typeof type === 'object' &&
|
||||
type !== null &&
|
||||
Object.keys(type).length === 0)
|
||||
) {
|
||||
info +=
|
||||
' You likely forgot to export your component from the file ' +
|
||||
"it's defined in, or you might have mixed up default and named imports.";
|
||||
}
|
||||
|
||||
const sourceInfo = getSourceInfoErrorAddendum(source);
|
||||
if (sourceInfo) {
|
||||
info += sourceInfo;
|
||||
} else {
|
||||
info += getDeclarationErrorAddendum();
|
||||
}
|
||||
|
||||
let typeString;
|
||||
if (type === null) {
|
||||
typeString = 'null';
|
||||
} else if (isArray(type)) {
|
||||
typeString = 'array';
|
||||
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
|
||||
typeString = `<${getComponentNameFromType(type.type) || 'Unknown'} />`;
|
||||
info =
|
||||
' Did you accidentally export a JSX literal instead of a component?';
|
||||
} else {
|
||||
typeString = typeof type;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
|
||||
'Explicitly pass a key after spreading props in your JSX call. ' +
|
||||
'E.g. <%s {...props} key={key} />',
|
||||
getComponentNameFromType(type) || 'ComponentName',
|
||||
'React.jsx: type is invalid -- expected a string (for ' +
|
||||
'built-in components) or a class/function (for composite ' +
|
||||
'components) but got: %s.%s',
|
||||
typeString,
|
||||
info,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === REACT_FRAGMENT_TYPE) {
|
||||
validateFragmentProps(element);
|
||||
} else {
|
||||
validatePropTypes(element);
|
||||
}
|
||||
const element = jsxDEV(type, props, key, source, self);
|
||||
|
||||
return element;
|
||||
// The result can be nullish if a mock or a custom function is used.
|
||||
// TODO: Drop this when these are no longer allowed as the type argument.
|
||||
if (element == null) {
|
||||
return element;
|
||||
}
|
||||
|
||||
// Skip key warning if the type isn't valid since our key validation logic
|
||||
// doesn't expect a non-string/function type and can throw confusing errors.
|
||||
// We don't want exception behavior to differ between dev and prod.
|
||||
// (Rendering will throw with a helpful message and as soon as the type is
|
||||
// fixed, the key warnings will appear.)
|
||||
|
||||
if (validType) {
|
||||
const children = props.children;
|
||||
if (children !== undefined) {
|
||||
if (isStaticChildren) {
|
||||
if (isArray(children)) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
validateChildKeys(children[i], type);
|
||||
}
|
||||
|
||||
if (Object.freeze) {
|
||||
Object.freeze(children);
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'React.jsx: Static children should always be an array. ' +
|
||||
'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
|
||||
'Use the Babel transform instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validateChildKeys(children, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (warnAboutSpreadingKeyToJSX) {
|
||||
if (hasOwnProperty.call(props, 'key')) {
|
||||
const componentName = getComponentNameFromType(type);
|
||||
const keys = Object.keys(props).filter(k => k !== 'key');
|
||||
const beforeExample =
|
||||
keys.length > 0
|
||||
? '{key: someKey, ' + keys.join(': ..., ') + ': ...}'
|
||||
: '{key: someKey}';
|
||||
if (!didWarnAboutKeySpread[componentName + beforeExample]) {
|
||||
const afterExample =
|
||||
keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';
|
||||
console.error(
|
||||
'A props object containing a "key" prop is being spread into JSX:\n' +
|
||||
' let props = %s;\n' +
|
||||
' <%s {...props} />\n' +
|
||||
'React keys must be passed directly to JSX without using spread:\n' +
|
||||
' let props = %s;\n' +
|
||||
' <%s key={someKey} {...props} />',
|
||||
beforeExample,
|
||||
componentName,
|
||||
afterExample,
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutKeySpread[componentName + beforeExample] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === REACT_FRAGMENT_TYPE) {
|
||||
validateFragmentProps(element);
|
||||
} else {
|
||||
validatePropTypes(element);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
// These two functions exist to still get child warnings in dev
|
||||
|
||||
@@ -9,6 +9,7 @@ PropTypes = null
|
||||
React = null
|
||||
ReactDOM = null
|
||||
ReactDOMClient = null
|
||||
ReactFeatureFlags = null
|
||||
act = null
|
||||
|
||||
describe 'ReactCoffeeScriptClass', ->
|
||||
@@ -22,6 +23,7 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
React = require 'react'
|
||||
ReactDOM = require 'react-dom'
|
||||
ReactDOMClient = require 'react-dom/client'
|
||||
ReactFeatureFlags = require 'shared/ReactFeatureFlags'
|
||||
act = require('jest-react').act
|
||||
PropTypes = require 'prop-types'
|
||||
container = document.createElement 'div'
|
||||
@@ -528,7 +530,7 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
|
||||
test React.createElement(Foo), 'DIV', 'bar-through-context'
|
||||
|
||||
it 'supports classic refs', ->
|
||||
it 'supports string refs', ->
|
||||
class Foo extends React.Component
|
||||
render: ->
|
||||
React.createElement(InnerComponent,
|
||||
@@ -537,7 +539,19 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
)
|
||||
|
||||
ref = React.createRef()
|
||||
test(React.createElement(Foo, ref: ref), 'DIV', 'foo')
|
||||
expect(->
|
||||
test(React.createElement(Foo, ref: ref), 'DIV', 'foo')
|
||||
).toErrorDev(
|
||||
if ReactFeatureFlags.warnAboutStringRefs
|
||||
then [
|
||||
'Warning: Component "Foo" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Foo (at **)'
|
||||
]
|
||||
else []
|
||||
);
|
||||
expect(ref.current.refs.inner.getName()).toBe 'foo'
|
||||
|
||||
it 'supports drilling through to the DOM using findDOMNode', ->
|
||||
|
||||
@@ -46,6 +46,8 @@ describe('ReactContextValidator', () => {
|
||||
};
|
||||
|
||||
class ComponentInFooBarContext extends React.Component {
|
||||
childRef = React.createRef();
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
foo: 'abc',
|
||||
@@ -54,7 +56,7 @@ describe('ReactContextValidator', () => {
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Component ref="child" />;
|
||||
return <Component ref={this.childRef} />;
|
||||
}
|
||||
}
|
||||
ComponentInFooBarContext.childContextTypes = {
|
||||
@@ -65,7 +67,7 @@ describe('ReactContextValidator', () => {
|
||||
const instance = ReactTestUtils.renderIntoDocument(
|
||||
<ComponentInFooBarContext />,
|
||||
);
|
||||
expect(instance.refs.child.context).toEqual({foo: 'abc'});
|
||||
expect(instance.childRef.current.context).toEqual({foo: 'abc'});
|
||||
});
|
||||
|
||||
it('should pass next context to lifecycles', () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ let PropTypes;
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactDOMClient;
|
||||
let ReactFeatureFlags;
|
||||
let act;
|
||||
|
||||
describe('ReactES6Class', () => {
|
||||
@@ -31,6 +32,7 @@ describe('ReactES6Class', () => {
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactDOMClient = require('react-dom/client');
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
act = require('jest-react').act;
|
||||
container = document.createElement('div');
|
||||
root = ReactDOMClient.createRoot(container);
|
||||
@@ -568,14 +570,26 @@ describe('ReactES6Class', () => {
|
||||
test(<Foo />, 'DIV', 'bar-through-context');
|
||||
});
|
||||
|
||||
it('supports classic refs', () => {
|
||||
it('supports string refs', () => {
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return <Inner name="foo" ref="inner" />;
|
||||
}
|
||||
}
|
||||
const ref = React.createRef();
|
||||
test(<Foo ref={ref} />, 'DIV', 'foo');
|
||||
expect(() => {
|
||||
test(<Foo ref={ref} />, 'DIV', 'foo');
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "Foo" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in Foo (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
expect(ref.current.refs.inner.getName()).toBe('foo');
|
||||
});
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ describe('ReactElement', () => {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Child ref="childElement" />
|
||||
<Child ref={React.createRef()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,8 +82,10 @@ describe('ReactElementClone', () => {
|
||||
|
||||
it('should keep the original ref if it is not overridden', () => {
|
||||
class Grandparent extends React.Component {
|
||||
yoloRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return <Parent child={<div ref="yolo" />} />;
|
||||
return <Parent child={<div ref={this.yoloRef} />} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +98,7 @@ describe('ReactElementClone', () => {
|
||||
}
|
||||
|
||||
const component = ReactTestUtils.renderIntoDocument(<Grandparent />);
|
||||
expect(component.refs.yolo.tagName).toBe('DIV');
|
||||
expect(component.yoloRef.current.tagName).toBe('DIV');
|
||||
});
|
||||
|
||||
it('should transfer the key property', () => {
|
||||
@@ -174,21 +176,25 @@ describe('ReactElementClone', () => {
|
||||
|
||||
it('should support keys and refs', () => {
|
||||
class Parent extends React.Component {
|
||||
xyzRef = React.createRef();
|
||||
|
||||
render() {
|
||||
const clone = React.cloneElement(this.props.children, {
|
||||
key: 'xyz',
|
||||
ref: 'xyz',
|
||||
ref: this.xyzRef,
|
||||
});
|
||||
expect(clone.key).toBe('xyz');
|
||||
expect(clone.ref).toBe('xyz');
|
||||
expect(clone.ref).toBe(this.xyzRef);
|
||||
return <div>{clone}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Grandparent extends React.Component {
|
||||
parentRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Parent ref="parent">
|
||||
<Parent ref={this.parentRef}>
|
||||
<span key="abc" />
|
||||
</Parent>
|
||||
);
|
||||
@@ -196,30 +202,37 @@ describe('ReactElementClone', () => {
|
||||
}
|
||||
|
||||
const component = ReactTestUtils.renderIntoDocument(<Grandparent />);
|
||||
expect(component.refs.parent.refs.xyz.tagName).toBe('SPAN');
|
||||
expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
|
||||
});
|
||||
|
||||
it('should steal the ref if a new ref is specified', () => {
|
||||
class Parent extends React.Component {
|
||||
xyzRef = React.createRef();
|
||||
|
||||
render() {
|
||||
const clone = React.cloneElement(this.props.children, {ref: 'xyz'});
|
||||
const clone = React.cloneElement(this.props.children, {
|
||||
ref: this.xyzRef,
|
||||
});
|
||||
return <div>{clone}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
class Grandparent extends React.Component {
|
||||
parentRef = React.createRef();
|
||||
childRef = React.createRef();
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Parent ref="parent">
|
||||
<span ref="child" />
|
||||
<Parent ref={this.parentRef}>
|
||||
<span ref={this.childRef} />
|
||||
</Parent>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const component = ReactTestUtils.renderIntoDocument(<Grandparent />);
|
||||
expect(component.refs.child).toBeUndefined();
|
||||
expect(component.refs.parent.refs.xyz.tagName).toBe('SPAN');
|
||||
expect(component.childRef).toEqual({current: null});
|
||||
expect(component.parentRef.current.xyzRef.current.tagName).toBe('SPAN');
|
||||
});
|
||||
|
||||
it('should overwrite props', () => {
|
||||
|
||||
@@ -280,7 +280,7 @@ describe('ReactElement.jsx', () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
return JSXRuntime.jsx('div', {
|
||||
children: JSXRuntime.jsx(Child, {ref: 'childElement'}),
|
||||
children: JSXRuntime.jsx(Child, {ref: React.createRef()}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -429,16 +429,19 @@ describe('ReactElement.jsx', () => {
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
return JSXRuntime.jsx('div', {
|
||||
children: [JSXRuntime.jsx(Child, {key: '0'})],
|
||||
children: [JSXRuntime.jsx(Child, {key: '0', prop: 'hi'})],
|
||||
});
|
||||
}
|
||||
}
|
||||
expect(() =>
|
||||
ReactDOM.render(JSXRuntime.jsx(Parent, {}), container),
|
||||
).toErrorDev(
|
||||
'Warning: React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
|
||||
'Explicitly pass a key after spreading props in your JSX call. ' +
|
||||
'E.g. <Child {...props} key={key} />',
|
||||
'Warning: A props object containing a "key" prop is being spread into JSX:\n' +
|
||||
' let props = {key: someKey, prop: ...};\n' +
|
||||
' <Child {...props} />\n' +
|
||||
'React keys must be passed directly to JSX without using spread:\n' +
|
||||
' let props = {prop: ...};\n' +
|
||||
' <Child key={someKey} {...props} />',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,10 +78,11 @@ describe('ReactJSXElement', () => {
|
||||
});
|
||||
|
||||
it('extracts key and ref from the rest of the props', () => {
|
||||
const element = <Component key="12" ref="34" foo="56" />;
|
||||
const ref = React.createRef();
|
||||
const element = <Component key="12" ref={ref} foo="56" />;
|
||||
expect(element.type).toBe(Component);
|
||||
expect(element.key).toBe('12');
|
||||
expect(element.ref).toBe('34');
|
||||
expect(element.ref).toBe(ref);
|
||||
const expectation = {foo: '56'};
|
||||
Object.freeze(expectation);
|
||||
expect(element.props).toEqual(expectation);
|
||||
|
||||
@@ -765,12 +765,18 @@ describe('string refs', () => {
|
||||
expect(() => {
|
||||
ReactDOM.render(<OuterComponent />, container);
|
||||
}).toErrorDev(
|
||||
'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
|
||||
'String refs are a source of potential bugs and should be avoided. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in OuterComponent (at **)',
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? 'Warning: Component "StrictMode" contains the string ref "somestring". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in OuterComponent (at **)'
|
||||
: 'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
|
||||
'String refs are a source of potential bugs and should be avoided. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in OuterComponent (at **)',
|
||||
);
|
||||
|
||||
// Dedup
|
||||
@@ -806,13 +812,20 @@ describe('string refs', () => {
|
||||
expect(() => {
|
||||
ReactDOM.render(<OuterComponent />, container);
|
||||
}).toErrorDev(
|
||||
'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
|
||||
'String refs are a source of potential bugs and should be avoided. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in InnerComponent (at **)\n' +
|
||||
' in OuterComponent (at **)',
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? 'Warning: Component "InnerComponent" contains the string ref "somestring". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in InnerComponent (at **)\n' +
|
||||
' in OuterComponent (at **)'
|
||||
: 'Warning: A string ref, "somestring", has been found within a strict mode tree. ' +
|
||||
'String refs are a source of potential bugs and should be avoided. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: ' +
|
||||
'https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in InnerComponent (at **)\n' +
|
||||
' in OuterComponent (at **)',
|
||||
);
|
||||
|
||||
// Dedup
|
||||
|
||||
@@ -17,6 +17,7 @@ import ReactDOMClient = require('react-dom/client');
|
||||
import ReactDOMTestUtils = require('react-dom/test-utils');
|
||||
import PropTypes = require('prop-types');
|
||||
import internalAct = require('jest-react');
|
||||
import ReactFeatureFlags = require('shared/ReactFeatureFlags')
|
||||
|
||||
// Before Each
|
||||
|
||||
@@ -686,9 +687,21 @@ describe('ReactTypeScriptClass', function() {
|
||||
test(React.createElement(ProvideContext), 'DIV', 'bar-through-context');
|
||||
});
|
||||
|
||||
it('supports classic refs', function() {
|
||||
it('supports string refs', function() {
|
||||
const ref = React.createRef();
|
||||
test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo');
|
||||
expect(() => {
|
||||
test(React.createElement(ClassicRefs, {ref: ref}), 'DIV', 'foo');
|
||||
}).toErrorDev(
|
||||
ReactFeatureFlags.warnAboutStringRefs
|
||||
? [
|
||||
'Warning: Component "ClassicRefs" contains the string ref "inner". ' +
|
||||
'Support for string refs will be removed in a future major release. ' +
|
||||
'We recommend using useRef() or createRef() instead. ' +
|
||||
'Learn more about using refs safely here: https://reactjs.org/link/strict-mode-string-ref\n' +
|
||||
' in ClassicRefs (at **)',
|
||||
]
|
||||
: [],
|
||||
);
|
||||
expect(ref.current.refs.inner.getName()).toBe('foo');
|
||||
});
|
||||
|
||||
|
||||
@@ -294,6 +294,8 @@ function validateFragmentProps(fragment) {
|
||||
}
|
||||
}
|
||||
|
||||
const didWarnAboutKeySpread = {};
|
||||
|
||||
export function jsxWithValidation(
|
||||
type,
|
||||
props,
|
||||
@@ -390,12 +392,29 @@ export function jsxWithValidation(
|
||||
|
||||
if (warnAboutSpreadingKeyToJSX) {
|
||||
if (hasOwnProperty.call(props, 'key')) {
|
||||
console.error(
|
||||
'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
|
||||
'Explicitly pass a key after spreading props in your JSX call. ' +
|
||||
'E.g. <%s {...props} key={key} />',
|
||||
getComponentNameFromType(type) || 'ComponentName',
|
||||
);
|
||||
const componentName = getComponentNameFromType(type);
|
||||
const keys = Object.keys(props).filter(k => k !== 'key');
|
||||
const beforeExample =
|
||||
keys.length > 0
|
||||
? '{key: someKey, ' + keys.join(': ..., ') + ': ...}'
|
||||
: '{key: someKey}';
|
||||
if (!didWarnAboutKeySpread[componentName + beforeExample]) {
|
||||
const afterExample =
|
||||
keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';
|
||||
console.error(
|
||||
'A props object containing a "key" prop is being spread into JSX:\n' +
|
||||
' let props = %s;\n' +
|
||||
' <%s {...props} />\n' +
|
||||
'React keys must be passed directly to JSX without using spread:\n' +
|
||||
' let props = %s;\n' +
|
||||
' <%s key={someKey} {...props} />',
|
||||
beforeExample,
|
||||
componentName,
|
||||
afterExample,
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutKeySpread[componentName + beforeExample] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,13 +205,13 @@ export const disableTextareaChildren = false;
|
||||
// Part of the simplification of React.createElement so we can eventually move
|
||||
// from React.createElement to React.jsx
|
||||
// https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false; // deprecate later, not 18.0
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true; // deprecate later, not 18.0
|
||||
|
||||
// Enables a warning when trying to spread a 'key' to an element;
|
||||
// a deprecated pattern we want to get rid of in the future
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Debugging and DevTools
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
// TODO: This module is used both by the release scripts and to expose a version
|
||||
// at runtime. We should instead inject the version number as part of the build
|
||||
// process, and use the ReactVersions.js module as the single source of truth.
|
||||
export default '18.1.0';
|
||||
export default '18.3.0';
|
||||
|
||||
@@ -39,14 +39,14 @@ export const warnAboutDeprecatedLifecycles = true;
|
||||
export const enableScopeAPI = false;
|
||||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
export const enableSuspenseAvoidThisFallback = false;
|
||||
export const enableSuspenseAvoidThisFallbackFizz = false;
|
||||
export const enableCPUSuspense = true;
|
||||
|
||||
@@ -29,14 +29,14 @@ export const enableSchedulerDebugging = false;
|
||||
export const enableScopeAPI = false;
|
||||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
export const enableSuspenseAvoidThisFallback = false;
|
||||
export const enableSuspenseAvoidThisFallbackFizz = false;
|
||||
export const enableCPUSuspense = false;
|
||||
|
||||
@@ -29,14 +29,14 @@ export const enableSchedulerDebugging = false;
|
||||
export const enableScopeAPI = false;
|
||||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
export const enableSuspenseAvoidThisFallback = false;
|
||||
export const enableSuspenseAvoidThisFallbackFizz = false;
|
||||
export const enableCPUSuspense = false;
|
||||
|
||||
@@ -29,14 +29,14 @@ export const enableSchedulerDebugging = false;
|
||||
export const enableScopeAPI = false;
|
||||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
export const enableComponentStackLocations = false;
|
||||
export const enableLegacyFBSupport = false;
|
||||
export const enableFilterEmptyStringAttributesDOM = false;
|
||||
|
||||
@@ -29,14 +29,14 @@ export const disableInputAttributeSyncing = false;
|
||||
export const enableScopeAPI = true;
|
||||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = true;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = true;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
export const enableSuspenseAvoidThisFallback = true;
|
||||
export const enableSuspenseAvoidThisFallbackFizz = false;
|
||||
export const enableCPUSuspense = false;
|
||||
|
||||
@@ -29,14 +29,14 @@ export const enableSchedulerDebugging = false;
|
||||
export const enableScopeAPI = false;
|
||||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
export const enableSuspenseAvoidThisFallback = false;
|
||||
export const enableSuspenseAvoidThisFallbackFizz = false;
|
||||
export const enableCPUSuspense = false;
|
||||
|
||||
@@ -29,14 +29,14 @@ export const enableSchedulerDebugging = false;
|
||||
export const enableScopeAPI = true;
|
||||
export const enableCreateEventHandleAPI = true;
|
||||
export const enableSuspenseCallback = true;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = __EXPERIMENTAL__;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = __EXPERIMENTAL__;
|
||||
export const disableModulePatternComponents = true;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
export const enableSuspenseAvoidThisFallback = true;
|
||||
export const enableSuspenseAvoidThisFallbackFizz = false;
|
||||
export const enableCPUSuspense = true;
|
||||
|
||||
@@ -63,8 +63,8 @@ export const enableSchedulingProfiler =
|
||||
export const enableSchedulerDebugging = true;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const disableLegacyContext = __EXPERIMENTAL__;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = true;
|
||||
export const enableGetInspectorDataForInstanceInProduction = false;
|
||||
|
||||
export const enableCache = true;
|
||||
|
||||
@@ -23,6 +23,24 @@ module.exports = function shouldIgnoreConsoleError(format, args) {
|
||||
// We haven't finished migrating our tests to use createRoot.
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
format.indexOf(
|
||||
'uses the legacy contextTypes API which is no longer supported and will be removed'
|
||||
) !== -1 ||
|
||||
format.indexOf(
|
||||
'uses the legacy childContextTypes API which is no longer supported and will be removed'
|
||||
) !== -1 ||
|
||||
format.indexOf('ReactDOMTestUtils is deprecated') !== -1 ||
|
||||
format.indexOf('`ReactDOMTestUtils.act` is deprecated') !== -1 ||
|
||||
format.indexOf('findDOMNode is deprecated and will be removed') !==
|
||||
-1 ||
|
||||
format.indexOf('unmountComponentAtNode is deprecated') !== -1
|
||||
) {
|
||||
// This is a backported warning. In `main`, there's a different warning
|
||||
// (and it's fully tested). Not going to bother upgrading all the tests
|
||||
// on this old release branch, so let's just silence it instead.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user