Turn on string ref deprecation warning for everybody (not codemoddable) (#25383)
## Summary Alternate to https://github.com/facebook/react/pull/25334 without any prod runtime changes i.e. the proposed codemod in https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0000-create-element-changes.md#deprecate-string-refs-and-remove-production-mode-_owner-field would not work. ## How did you test this change? - [x] CI - [x] `yarn test` with and without `warnAboutStringRefs`
This commit is contained in:
parent
07f46ecf2e
commit
6fb8133ed3
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -36,6 +36,8 @@ describe('ReactDOMServerHydration', () => {
|
|||
let numClicks = 0;
|
||||
|
||||
class TestComponent extends React.Component {
|
||||
spanRef = React.createRef();
|
||||
|
||||
componentDidMount() {
|
||||
mountCount++;
|
||||
}
|
||||
|
@ -46,7 +48,7 @@ describe('ReactDOMServerHydration', () => {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<span ref="span" onClick={this.click}>
|
||||
<span ref={this.spanRef} onClick={this.click}>
|
||||
Name: {this.props.name}
|
||||
</span>
|
||||
);
|
||||
|
@ -89,7 +91,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);
|
||||
|
@ -107,7 +109,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);
|
||||
|
|
|
@ -221,13 +221,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>
|
||||
);
|
||||
}
|
||||
|
@ -237,12 +241,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', () => {
|
||||
|
@ -348,12 +352,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>
|
||||
|
@ -373,7 +378,7 @@ describe('ReactTestUtils', () => {
|
|||
container,
|
||||
);
|
||||
|
||||
const node = instance.refs.input;
|
||||
const node = instance.inputRef.current;
|
||||
node.value = 'zebra';
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -1385,25 +1385,36 @@ describe('ResponderEventPlugin', () => {
|
|||
const ReactDOMComponentTree = require('react-dom-bindings/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;
|
||||
|
|
|
@ -97,6 +97,10 @@ if (__DEV__) {
|
|||
};
|
||||
}
|
||||
|
||||
function isReactClass(type) {
|
||||
return type.prototype && type.prototype.isReactComponent;
|
||||
}
|
||||
|
||||
function coerceRef(
|
||||
returnFiber: Fiber,
|
||||
current: Fiber | null,
|
||||
|
@ -120,7 +124,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';
|
||||
|
|
|
@ -97,6 +97,10 @@ if (__DEV__) {
|
|||
};
|
||||
}
|
||||
|
||||
function isReactClass(type) {
|
||||
return type.prototype && type.prototype.isReactComponent;
|
||||
}
|
||||
|
||||
function coerceRef(
|
||||
returnFiber: Fiber,
|
||||
current: Fiber | null,
|
||||
|
@ -120,7 +124,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';
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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} />, {
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
@ -93,7 +93,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', () => {
|
||||
|
|
|
@ -221,7 +221,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()}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -926,12 +926,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
|
||||
|
@ -967,13 +973,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');
|
||||
});
|
||||
|
||||
|
|
|
@ -219,7 +219,7 @@ export const warnAboutDefaultPropsOnFunctionComponents = false; // deprecate lat
|
|||
// a deprecated pattern we want to get rid of in the future
|
||||
export const warnAboutSpreadingKeyToJSX = true;
|
||||
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Debugging and DevTools
|
||||
|
|
|
@ -42,7 +42,7 @@ export const enableScopeAPI = false;
|
|||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
|
|
@ -32,7 +32,7 @@ export const enableScopeAPI = false;
|
|||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
|
|
@ -32,7 +32,7 @@ export const enableScopeAPI = false;
|
|||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
|
|
@ -32,7 +32,7 @@ export const enableScopeAPI = false;
|
|||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
|
|
@ -32,7 +32,7 @@ export const enableScopeAPI = true;
|
|||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = true;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
|
|
@ -32,7 +32,7 @@ export const enableScopeAPI = false;
|
|||
export const enableCreateEventHandleAPI = false;
|
||||
export const enableSuspenseCallback = false;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = false;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
|
|
@ -32,7 +32,7 @@ export const enableScopeAPI = true;
|
|||
export const enableCreateEventHandleAPI = true;
|
||||
export const enableSuspenseCallback = true;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const disableLegacyContext = __EXPERIMENTAL__;
|
||||
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
|
|
@ -66,7 +66,7 @@ export const enableSchedulingProfiler: boolean =
|
|||
export const enableSchedulerDebugging = true;
|
||||
export const warnAboutDeprecatedLifecycles = true;
|
||||
export const disableLegacyContext = __EXPERIMENTAL__;
|
||||
export const warnAboutStringRefs = false;
|
||||
export const warnAboutStringRefs = true;
|
||||
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
||||
export const enableGetInspectorDataForInstanceInProduction = false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue