ref.spec.jsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. import ReactTestUtils from "../../lib/ReactTestUtils";
  2. import React from '../../src/May';
  3. import { render } from '../../src/may-dom/MayDom'
  4. var ReactDOM = {
  5. render: render
  6. }
  7. React.render = render;
  8. // import React from "../../dist/ReactANU";
  9. // var React = require('react');//hyphenate
  10. // var ReactDOM = require('react-dom');
  11. //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js
  12. // var ReactDOM = window.ReactDOM || React;
  13. describe("ref", function () {
  14. // this.timeout(200000);
  15. var body = document.body,
  16. div;
  17. beforeEach(function () {
  18. div = document.createElement("div");
  19. body.appendChild(div);
  20. });
  21. afterEach(function () {
  22. body.removeChild(div);
  23. });
  24. it("patchRef", function () {
  25. class App extends React.Component {
  26. constructor(props) {
  27. super(props);
  28. this.handleClick = this
  29. .handleClick
  30. .bind(this);
  31. }
  32. handleClick() {
  33. // Explicitly focus the text input using the raw DOM API.
  34. if (this.myTextInput !== null) {
  35. this
  36. .myTextInput
  37. .focus();
  38. }
  39. }
  40. render() {
  41. return (
  42. <div>
  43. <input type="text" ref={(ref) => this.myTextInput = ref} />
  44. <input
  45. ref='a'
  46. type="button"
  47. value="Focus the text input"
  48. onClick={this.handleClick} />
  49. </div>
  50. );
  51. }
  52. }
  53. var s = ReactDOM.render(<App />, div);
  54. var dom = s.refs.a;
  55. ReactTestUtils.Simulate.click(dom);
  56. expect(document.activeElement).toBe(s.myTextInput);
  57. expect(s.myTextInput).toBeDefined();
  58. });
  59. it("patchRef Component", function() {
  60. class App extends React.Component {
  61. render() {
  62. return <div title='1'><A ref='a' /></div>;
  63. }
  64. }
  65. var index = 1;
  66. class A extends React.Component {
  67. componentWillReceiveProps() {
  68. index = 0;
  69. this.forceUpdate();
  70. }
  71. render() {
  72. return index
  73. ? <strong>111</strong>
  74. : <em>111</em>;
  75. }
  76. }
  77. var s = ReactTestUtils.renderIntoDocument(<App />);
  78. expect(s.refs.a instanceof A).toBe(true);
  79. });
  80. it("没有组件的情况", function() {
  81. var index = 0;
  82. function ref(a) {
  83. index ++;
  84. expect(a.tagName).toBe("DIV");
  85. }
  86. var s = ReactTestUtils.renderIntoDocument(<div ref={ref}></div>);
  87. expect(index).toBe(1);
  88. });
  89. it("should invoke refs in Component.render()", function() {
  90. var i = 0;
  91. let outer = function (a) {
  92. expect(a).toBe(div.firstChild);
  93. i++;
  94. };
  95. let inner = function (a) {
  96. expect(a).toBe(div.firstChild.firstChild);
  97. i++;
  98. };
  99. class Foo extends React.Component {
  100. render() {
  101. return (
  102. <div ref={outer}>
  103. <span ref={inner} />
  104. </div>
  105. );
  106. }
  107. }
  108. ReactDOM.render(<Foo />, div);
  109. expect(i).toBe(2);
  110. });
  111. it("rener方法在存在其他组件,那么组件以innerHTML方式引用子节点,子节点有ref", function() {
  112. class App extends React.Component {
  113. constructor(props) {
  114. super(props);
  115. this.state = {
  116. tip: "g-up-tips",
  117. text: "xxxx"
  118. };
  119. }
  120. render() {
  121. return <div ref='parent' className='parent'>
  122. <Child ref='child'>
  123. <span ref="inner" className="inner">child</span>
  124. </Child>
  125. </div>;
  126. }
  127. }
  128. class Child extends React.Component {
  129. constructor(props) {
  130. super(props);
  131. this.state = {};
  132. }
  133. render() {
  134. return <div className='child'>{this.props.children}</div>;
  135. }
  136. }
  137. var s = ReactTestUtils.renderIntoDocument(<App />);
  138. expect(Object.keys(s.refs).sort()).toEqual(["child", "inner", "parent"]);
  139. });
  140. it("用户在构造器里生成虚拟DOM", function() {
  141. var a;
  142. class App extends React.Component {
  143. constructor(props) {
  144. super(props);
  145. this.sliderLeftJSX = this.renderSlider("btnLeft");
  146. this.state = {};
  147. }
  148. renderSlider(which = "btnLeft") {
  149. return (<span ref={dom => {
  150. this[which] = dom;
  151. }} />);
  152. }
  153. componentDidMount() {
  154. a = !!this.btnLeft;
  155. }
  156. render() {
  157. return (
  158. <div >
  159. <div className="track" ref="track">
  160. {this.sliderLeftJSX}
  161. </div>
  162. </div>
  163. );
  164. }
  165. }
  166. var s = ReactTestUtils.renderIntoDocument(<App/>);
  167. expect(a).toBe(true);
  168. });
  169. it("Stateless组件也会被执行", function() {
  170. var b;
  171. function App() {
  172. return (
  173. <div >StateLess</div>
  174. );
  175. }
  176. function refFn(a){
  177. b = a;
  178. }
  179. ReactTestUtils.renderIntoDocument(<App ref={refFn} />);
  180. expect(b).toBe(null);
  181. });
  182. it("ReactDOM.render中的元素也会被执行", function() {
  183. var b;
  184. function refFn(a){
  185. b = a;
  186. }
  187. ReactTestUtils.renderIntoDocument(<h1 ref={refFn}/>);
  188. expect(b && b.tagName).toBe("H1");
  189. });
  190. it("带ref的组件被子组件cloneElement", function() {
  191. class Select extends React.Component {
  192. constructor(props) {
  193. super(props);
  194. this.state = {
  195. aaa: 1
  196. };
  197. }
  198. render() {
  199. return React.createElement(SelectTrigger, {
  200. ref: "trigger"
  201. }, React.createElement(
  202. "div",
  203. {
  204. ref: "root"
  205. }, "xxxx"));
  206. }
  207. }
  208. class SelectTrigger extends React.Component {
  209. constructor(props) {
  210. super(props);
  211. this.state = {
  212. aaa: 2
  213. };
  214. }
  215. render() {
  216. return React.createElement(Trigger, Object.assign({ title: "xxx" }, this.props), this.props.children);
  217. }
  218. }
  219. class Trigger extends React.Component {
  220. constructor(props) {
  221. super(props);
  222. this.state = {
  223. aaa: 2
  224. };
  225. }
  226. render() {
  227. var props = this.props;
  228. var children = props.children;
  229. var child = React.Children.only(children);
  230. return React.cloneElement(child, { className: "5555" });
  231. }
  232. }
  233. var s = React.render(<Select />, div);
  234. expect(typeof s.refs.root).toBe("object");
  235. expect(typeof s.refs.trigger).toBe("object");
  236. });
  237. it("相同位置上的元素节点的string ref值不一样", function() {
  238. class Foo extends React.Component {
  239. render() {
  240. return (
  241. <div>
  242. {this.props.a ? <span ref="aaa" className="aaa"/>: <span ref="bbb" className="bbb" />}
  243. </div>
  244. );
  245. }
  246. }
  247. var s = ReactDOM.render(<Foo a={1}/>, div);
  248. expect(s.refs.aaa.className).toBe("aaa");
  249. ReactDOM.render(<Foo a={0}/>, div);
  250. expect(s.refs.aaa).toBe(undefined);
  251. expect(s.refs.bbb.className).toBe("bbb");
  252. });
  253. it("相同位置上的元素节点的ref类型不一样", function() {
  254. var a = 1;
  255. class Foo extends React.Component {
  256. render() {
  257. return (
  258. <div>
  259. {this.props.a ? <span ref="aaa" className="aaa"/>: <span ref={function(b){
  260. a = b;
  261. }} className="bbb" />}
  262. </div>
  263. );
  264. }
  265. }
  266. var s = ReactDOM.render(<Foo a={1}/>, div);
  267. expect(s.refs.aaa.className).toBe("aaa");
  268. ReactDOM.render(<Foo a={0}/>, div);
  269. expect(s.refs.aaa).toBe(undefined);
  270. expect(typeof a).toBe("object");
  271. });
  272. it("为元素添加ref", function() {
  273. class Foo extends React.Component {
  274. render() {
  275. return (
  276. <div>
  277. {this.props.a ? <span className="aaa"/>: <span ref="aaa" className="bbb" />}
  278. </div>
  279. );
  280. }
  281. }
  282. var s = ReactDOM.render(<Foo a={1}/>, div);
  283. expect(s.refs).toEqual({});
  284. ReactDOM.render(<Foo a={0}/>, div);
  285. expect(typeof s.refs.aaa).toBe("object");
  286. });
  287. it("相同位置上的元素节点的ref函数不一样", function() {
  288. var log = [];
  289. class Foo extends React.Component {
  290. render() {
  291. return (
  292. <div>
  293. {this.props.a ? <span ref={function(a){
  294. log.push(a);
  295. }} className="aaa"/>: <b ref={function(a){
  296. log.push(a);
  297. }} className="bbb" />}
  298. </div>
  299. );
  300. }
  301. }
  302. ReactDOM.render(<Foo a={1}/>, div);
  303. ReactDOM.render(<Foo a={0}/>, div);
  304. expect(log.length).toBe(3);
  305. expect(log[1]).toBe(null);
  306. expect(log[0].nodeName).toBe("SPAN");
  307. expect(log[2].nodeName).toBe("B");
  308. });
  309. it("相同位置上的元素节点的ref函数一样", function() {
  310. var log = [];
  311. function refFn(a){
  312. log.push(a);
  313. }
  314. class Foo extends React.Component {
  315. render() {
  316. return (
  317. <div>
  318. {this.props.a ? <span ref={refFn} className="aaa"/>: <b ref={refFn} className="bbb" />}
  319. </div>
  320. );
  321. }
  322. }
  323. ReactDOM.render(<Foo a={1}/>, div);
  324. ReactDOM.render(<Foo a={0}/>, div);
  325. expect(log.length).toBe(3);
  326. expect(log[1]).toBe(null);
  327. expect(log[0].nodeName).toBe("SPAN");
  328. expect(log[2].nodeName).toBe("B");
  329. });
  330. it("组件虚拟DOM的ref总在componentDidMount/Update后才执行", function() {
  331. var list = [];
  332. class Static extends React.Component {
  333. componentDidMount(){
  334. list.push("static did mount");
  335. }
  336. componentWillUpdate(){
  337. list.push("static will update");
  338. }
  339. componentDidUpdate(){
  340. list.push("static did update");
  341. }
  342. render() {
  343. list.push("static render");
  344. return <div>{this.props.children}</div>;
  345. }
  346. }
  347. class Component extends React.Component {
  348. render() {
  349. if (this.props.flipped) {
  350. return (
  351. <div>
  352. <Static ref={function(b){
  353. list.push(b === null ? "null 222":"instance 222");
  354. }}>
  355. B
  356. </Static>
  357. </div>
  358. );
  359. } else {
  360. return (
  361. <div>
  362. <Static ref={function(a){
  363. list.push(a === null ? "null 111":"instance 111");
  364. }} >
  365. A
  366. </Static>
  367. </div>
  368. );
  369. }
  370. }
  371. }
  372. var container = document.createElement("div");
  373. ReactDOM.render(<Component flipped={false} />, container);
  374. ReactDOM.render(<Component flipped={true} />, container);
  375. expect(list).toEqual([
  376. "static render",
  377. "static did mount",
  378. "instance 111",
  379. "null 111",
  380. "static will update",
  381. "static render",
  382. "static did update",
  383. "instance 222",
  384. ]);
  385. });
  386. it("先执行匹配元素的detach ref,然后卸载组件,最后attach ref",function(){
  387. var list = [];
  388. class A extends React.Component{
  389. componentWillUnmount(){
  390. list.push("remove");
  391. }
  392. render(){
  393. return <span>A</span>;
  394. }
  395. }
  396. class App extends React.Component {
  397. constructor(props) {
  398. super(props);
  399. this.state = {
  400. a: 1
  401. };
  402. }
  403. render() {
  404. return <div>
  405. {
  406. this.state.a ?
  407. [<A />,<A />,<A />,<span key="a" ref={(a)=>{
  408. list.push(111+ (a ? "instance": "null"));
  409. }}>a</span>,<span key="b" ref={(a)=>{
  410. list.push(222 + (a ? "instance": "null"));
  411. }}>b</span>]:
  412. [<span key="b" ref={(a)=>{
  413. list.push(333 + (a ? "instance": "null"));
  414. }}>b</span>,<span key="a" ref={(a)=>{
  415. list.push(444 + (a ? "instance": "null"));
  416. }}>a</span>]
  417. }
  418. </div>;
  419. }
  420. }
  421. var s = ReactDOM.render(<App />, div);
  422. s.setState({a: 0});
  423. expect(list).toEqual([
  424. "111instance",
  425. "222instance",
  426. "222null",
  427. "111null",
  428. "remove",
  429. "remove",
  430. "remove",
  431. "333instance",
  432. "444instance"
  433. ]);
  434. });
  435. it("先执行匹配元素的detach ref,然后更新卸载组件,最后attach ref",function(){
  436. var list = [];
  437. class A extends React.Component{
  438. componentWillUnmount(){
  439. list.push("A remove");
  440. }
  441. componentWillUpdate(){
  442. list.push("A update");
  443. }
  444. render(){
  445. return <span>A</span>;
  446. }
  447. }
  448. class B extends React.Component{
  449. componentDidMount(){
  450. list.push("B mount");
  451. }
  452. render(){
  453. return <span>B</span>;
  454. }
  455. }
  456. var list = [];
  457. class App extends React.Component {
  458. constructor(props) {
  459. super(props);
  460. this.state = {
  461. a: 1
  462. };
  463. }
  464. render() {
  465. return <div>
  466. {
  467. this.state.a ?
  468. [<A />,<A />,<A />,
  469. <span key="a" ref={(a)=>{
  470. list.push(111+(a?"instance":"null"));
  471. }}>a</span>,
  472. <span key="b" ref={(a)=>{
  473. list.push(222+(a?"instance":"null"));
  474. }}>b</span>,
  475. <span key="c" ref={(a)=>{
  476. list.push(333+(a?"instance":"null"));
  477. }}>c</span>,
  478. ]:
  479. [
  480. <span key="b" ref={(a)=>{
  481. list.push(444+(a?"instance":"null"));
  482. }}>b</span>,
  483. <span key="a" ref={(a)=>{
  484. list.push(555+(a?"instance":"null"));
  485. }}>a</span>,<A />, <B />]
  486. }
  487. </div>;
  488. }
  489. }
  490. var s = ReactDOM.render(<App />, div);
  491. s.setState({a: 0});
  492. expect(list).toEqual([
  493. "111instance",
  494. "222instance",
  495. "333instance",
  496. "222null",
  497. "111null",
  498. "A update",
  499. "A remove",
  500. "A remove",
  501. "333null",
  502. "444instance",
  503. "555instance",
  504. "B mount"
  505. ]);
  506. });
  507. it("ref与生命周期的执行顺序,更新后没有key",function(){
  508. var list = [];
  509. var A = class extends React.Component {
  510. componentWillMount() {
  511. list.push(this.props.name + " componentWillMount");
  512. }
  513. render() {
  514. return <div />;
  515. }
  516. componentDidMount() {
  517. list.push(this.props.name + " componentDidMount");
  518. }
  519. componentDidUpdate() {
  520. list.push(this.props.name + " componentDidUpdate");
  521. }
  522. componentWillUnmount() {
  523. list.push(this.props.name + " componentWillUnmount");
  524. }
  525. };
  526. var B = class extends React.Component {
  527. componentWillMount() {
  528. list.push(this.props.name + " componentWillMount");
  529. }
  530. render() {
  531. return <strong />;
  532. }
  533. componentDidMount() {
  534. list.push(this.props.name + " componentDidMount");
  535. }
  536. componentWillUnmount() {
  537. list.push(this.props.name + " componentWillUnmount");
  538. }
  539. };
  540. ReactDOM.render(
  541. <div>
  542. <A key="aa" name="a" ref={(a)=>{
  543. list.push("a "+!!a)
  544. ;
  545. }}></A>
  546. <A key="bb" name="b" ref={(a)=>{
  547. list.push("b "+!!a)
  548. ;
  549. }}></A>
  550. </div>,
  551. div
  552. );
  553. list.push("update...");
  554. ReactDOM.render(
  555. <div>
  556. <B name="c" ref={(a)=>{
  557. list.push("c "+!!a);
  558. }}></B>
  559. <B name="d" ref={(a)=>{
  560. list.push("d "+!!a)
  561. ;
  562. }}></B>
  563. </div>,
  564. div
  565. );
  566. expect(list).toEqual([
  567. "a componentWillMount",
  568. "b componentWillMount",
  569. "a componentDidMount",
  570. "a true",
  571. "b componentDidMount",
  572. "b true",
  573. "update...",
  574. "c componentWillMount",
  575. "d componentWillMount",
  576. "a false",
  577. "a componentWillUnmount",
  578. "b false",
  579. "b componentWillUnmount",
  580. "c componentDidMount",
  581. "c true",
  582. "d componentDidMount",
  583. "d true"
  584. ]);
  585. });
  586. it("ref与生命周期的执行顺序,更新后有key",function(){
  587. var list = [];
  588. var A = class extends React.Component {
  589. componentWillMount() {
  590. list.push(this.props.name + " componentWillMount");
  591. }
  592. render() {
  593. return <div />;
  594. }
  595. componentDidMount() {
  596. list.push(this.props.name + " componentDidMount");
  597. }
  598. componentDidUpdate() {
  599. list.push(this.props.name + " componentDidUpdate");
  600. }
  601. componentWillUnmount() {
  602. list.push(this.props.name + " componentWillUnmount");
  603. }
  604. };
  605. var B = class extends React.Component {
  606. componentWillMount() {
  607. list.push(this.props.name + " componentWillMount");
  608. }
  609. render() {
  610. return <strong />;
  611. }
  612. componentDidMount() {
  613. list.push(this.props.name + " componentDidMount");
  614. }
  615. componentWillUnmount() {
  616. list.push(this.props.name + " componentWillUnmount");
  617. }
  618. };
  619. ReactDOM.render(
  620. <div>
  621. <A key="aa" name="a" ref={(a)=>{
  622. list.push("a "+!!a)
  623. ;
  624. }}></A>
  625. <A key="bb" name="b" ref={(a)=>{
  626. list.push("b "+!!a)
  627. ;
  628. }}></A>
  629. </div>,
  630. div
  631. );
  632. list.push("update...");
  633. ReactDOM.render(
  634. <div>
  635. <B key="aa" name="c" ref={(a)=>{
  636. list.push("c "+!!a);
  637. }}></B>
  638. <B key="bb" name="d" ref={(a)=>{
  639. list.push("d "+!!a)
  640. ;
  641. }}></B>
  642. </div>,
  643. div
  644. );
  645. // "update...", "c componentWillMount", "d componentWillMount", "a false",
  646. //"a componentWillUnmount", "b false", "b componentWillUnmount",
  647. // expect(list).toEqual([
  648. // "a componentWillMount",
  649. // "b componentWillMount",
  650. // "a componentDidMount",
  651. // "a true",
  652. // "b componentDidMount",
  653. // "b true",
  654. // "update...",
  655. // "a false",
  656. // "a componentWillUnmount",
  657. // "c componentWillMount",
  658. // "b false",
  659. // "b componentWillUnmount",
  660. // "d componentWillMount",
  661. // "c componentDidMount",
  662. // "c true",
  663. // "d componentDidMount",
  664. // "d true"
  665. // ]);
  666. });
  667. });