lifecycle.spec.jsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. import ReactTestUtils from "../../lib/ReactTestUtils";
  2. import React from '../../src/May';
  3. import { render, unmountComponentAtNode, findDOMNode } from '../../src/may-dom/MayDom'
  4. var ReactDOM = {
  5. render: render,
  6. unmountComponentAtNode: unmountComponentAtNode,
  7. findDOMNode: findDOMNode
  8. }
  9. React.render = render;
  10. // import React from "../../dist/ReactANU";
  11. // var ReactDOM = React;
  12. // var ReactTestUtils = { Simulate: {} };
  13. // "click,change,keyDown,keyUp,KeyPress,mouseDown,mouseUp,mouseMove".replace(/\w+/g, function (name) {
  14. // ReactTestUtils.Simulate[name] = function (node, opts) {
  15. // if (!node || node.nodeType !== 1) {
  16. // throw "第一个参数必须为元素节点";
  17. // }
  18. // var fakeNativeEvent = opts || {};
  19. // fakeNativeEvent.target = node;
  20. // fakeNativeEvent.simulated = true;
  21. // fakeNativeEvent.type = name.toLowerCase();
  22. // React.eventSystem.dispatchEvent(fakeNativeEvent, name.toLowerCase());
  23. // };
  24. // });
  25. describe("生命周期例子", function () {
  26. // this.timeout(200000);
  27. var body = document.body,
  28. div;
  29. beforeEach(function () {
  30. div = document.createElement("div");
  31. body.appendChild(div);
  32. });
  33. afterEach(function () {
  34. body.removeChild(div);
  35. });
  36. it("如果在componentDidMount中调用setState方法\n那么setState的所有回调,\n都会延迟到componentDidUpdate中执行", function() {
  37. var list = [];
  38. class App extends React.Component {
  39. constructor(props) {
  40. super(props);
  41. this.state = {
  42. aaa: "aaa"
  43. };
  44. }
  45. componentWillMount() {
  46. this.setState(
  47. {
  48. aaa: "bbb"
  49. },
  50. function() {
  51. list.push("1111");
  52. }
  53. );
  54. }
  55. componentDidMount() {
  56. this.setState(
  57. {
  58. aaa: "cccc"
  59. },
  60. function() {
  61. list.push("2222");
  62. }
  63. );
  64. this.setState(
  65. {
  66. aaa: "dddd"
  67. },
  68. function() {
  69. list.push("3333");
  70. }
  71. );
  72. list.push("did mount");
  73. }
  74. componentWillUpdate() {
  75. list.push("will update");
  76. }
  77. componentDidUpdate() {
  78. list.push("did update");
  79. }
  80. render() {
  81. list.push(this.state.aaa);
  82. return <div>{this.state.aaa}</div>;
  83. }
  84. }
  85. var s = ReactDOM.render(<App />, div);
  86. expect(list.join("-")).toBe(
  87. "bbb-did mount-will update-dddd-did update-1111-2222-3333"
  88. );
  89. });
  90. it("父组件没有DidMount之时被子组件在willMount钩子里调用其setState", function () {
  91. var list = [];
  92. class App extends React.Component {
  93. constructor(props) {
  94. super(props);
  95. this.state = {
  96. aaa: "app render"
  97. };
  98. }
  99. componentWillMount() {
  100. list.push("app will mount");
  101. }
  102. componentDidMount() {
  103. list.push("app did mount");
  104. }
  105. componentWillUpdate() {
  106. list.push("app will update");
  107. }
  108. componentDidUpdate() {
  109. list.push("app did update");
  110. }
  111. render() {
  112. list.push(this.state.aaa);
  113. return (
  114. <div>
  115. <A parent={this} />
  116. {this.state.aaa}
  117. </div>
  118. );
  119. }
  120. }
  121. class A extends React.Component {
  122. componentWillMount() {
  123. this.props.parent.setState({
  124. aaa: "app new render"
  125. });
  126. this.props.parent.setState({
  127. aaa: "app new render2"
  128. });
  129. }
  130. componentWillReceiveProps() {
  131. list.push("child receive");
  132. }
  133. render() {
  134. return <p>A</p>;
  135. }
  136. }
  137. var s = ReactDOM.render(<App />, div);
  138. expect(list.join("-")).toBe(
  139. "app will mount-app render-app did mount-app will update-app new render2-child receive-app did update"
  140. );
  141. });
  142. it("父组件DidMount之时被子组件在componentWillReceiveProps钩子里调用其setState\n父组件的再次render会待到这次render完才调起", function () {
  143. var list = [];
  144. class App extends React.Component {
  145. constructor(props) {
  146. super(props);
  147. this.state = {
  148. aaa: "app render"
  149. };
  150. }
  151. componentWillMount() {
  152. list.push("app will mount");
  153. }
  154. componentDidMount() {
  155. this.setState({
  156. aaa: "app render1"
  157. });
  158. list.push("app did mount");
  159. }
  160. componentDidUpdate() {
  161. list.push("app did update");
  162. }
  163. render() {
  164. list.push(this.state.aaa);
  165. return (
  166. <div>
  167. <A parent={this} />
  168. {this.state.aaa}
  169. <C />
  170. </div>
  171. );
  172. }
  173. }
  174. var a = 1;
  175. class C extends React.Component {
  176. render() {
  177. list.push("C render");
  178. return <p>C</p>;
  179. }
  180. }
  181. class A extends React.Component {
  182. componentWillReceiveProps() {
  183. if (a < 2) {
  184. this.props.parent.setState(
  185. {
  186. aaa: "child call app render " + ++a
  187. },
  188. function () {
  189. list.push("componentWillReceiveProps 1");
  190. }
  191. );
  192. this.props.parent.setState(
  193. {
  194. aaa: "child call app render " + ++a
  195. },
  196. function () {
  197. list.push("componentWillReceiveProps 2");
  198. }
  199. );
  200. }
  201. }
  202. componentWillUpdate() {
  203. list.push("child will update");
  204. }
  205. render() {
  206. list.push("child render");
  207. return <p>A</p>;
  208. }
  209. }
  210. React.render(<App />, div);
  211. var list2 = [
  212. "app will mount",
  213. "app render",
  214. "child render",
  215. "C render",
  216. "app did mount",
  217. "app render1",
  218. "child will update",
  219. "child render",
  220. "C render",
  221. "app did update",
  222. "child call app render 3",
  223. "child will update",
  224. "child render",
  225. "C render",
  226. "app did update",
  227. "componentWillReceiveProps 1",
  228. "componentWillReceiveProps 2"
  229. ];
  230. expect(list.join("-")).toBe(list2.join("-"));
  231. });
  232. it("第一次渲染时不会触发componentWillUpdate", function() {
  233. var a = 1;
  234. class ReceivePropsComponent extends React.Component {
  235. componentWillUpdate() {
  236. a = 2;
  237. }
  238. render() {
  239. return <div />;
  240. }
  241. }
  242. React.render(<ReceivePropsComponent />, div);
  243. expect(a).toBe(1);
  244. });
  245. it("先执行子组件的mount钩子再到父组件的mount钩子", function() {
  246. let log = [];
  247. class Inner extends React.Component {
  248. componentDidMount() {
  249. log.push("inner");
  250. }
  251. render() {
  252. return <div id="inner" />;
  253. }
  254. }
  255. class Outer extends React.Component {
  256. componentDidMount() {
  257. log.push("outer");
  258. }
  259. render(props) {
  260. return <Inner />;
  261. }
  262. }
  263. React.render(<Outer />, div);
  264. expect(log.join("-")).toBe("inner-outer");
  265. });
  266. it("在componentWillMount中使用setState", function() {
  267. var list = [];
  268. class App extends React.Component {
  269. constructor(props) {
  270. super(props);
  271. this.state = {
  272. aaa: 111
  273. };
  274. }
  275. componentWillMount() {
  276. this.setState(
  277. {
  278. aaa: 222
  279. },
  280. function() {
  281. list.push("555");
  282. }
  283. );
  284. this.setState(
  285. {
  286. aaa: 333
  287. },
  288. function() {
  289. list.push("666");
  290. }
  291. );
  292. }
  293. render() {
  294. list.push(this.state.aaa);
  295. return <p>{this.state.aaa}</p>;
  296. }
  297. }
  298. var s = React.render(<App />, div);
  299. expect(list.join("-")).toBe("333-555-666");
  300. expect(div.textContent || div.innerText).toBe("333");
  301. });
  302. it("在componentWillMount中使用setState", function() {
  303. var list = [];
  304. class App extends React.Component {
  305. constructor(props) {
  306. super(props);
  307. this.state = {
  308. aaa: 111
  309. };
  310. }
  311. componentWillMount() {
  312. this.setState(
  313. {
  314. aaa: 222
  315. },
  316. function() {
  317. list.push("555");
  318. }
  319. );
  320. this.setState(
  321. {
  322. aaa: 333
  323. },
  324. function() {
  325. list.push("666");
  326. }
  327. );
  328. }
  329. render() {
  330. list.push(this.state.aaa);
  331. return <p>{this.state.aaa}</p>;
  332. }
  333. }
  334. var s = React.render(<App />, div);
  335. expect(list.join("-")).toBe("333-555-666");
  336. expect(div.textContent || div.innerText).toBe("333");
  337. });
  338. it("在componentDidMount中使用setState,会导致willMount, DidMout中的回调都延后",function() {
  339. var list = [];
  340. class App extends React.Component {
  341. constructor(props) {
  342. super(props);
  343. this.state = {
  344. aaa: 111
  345. };
  346. }
  347. componentWillMount() {
  348. this.setState(
  349. {
  350. aaa: 222
  351. },
  352. function() {
  353. list.push("555");
  354. }
  355. );
  356. this.setState(
  357. {
  358. aaa: 333
  359. },
  360. function() {
  361. list.push("666");
  362. }
  363. );
  364. }
  365. componentDidMount() {
  366. this.setState(
  367. {
  368. aaa: 444
  369. },
  370. function() {
  371. list.push("777");
  372. }
  373. );
  374. }
  375. render() {
  376. list.push(this.state.aaa);
  377. return <p>{this.state.aaa}</p>;
  378. }
  379. }
  380. var s = React.render(<App />, div);
  381. expect(list.join("-")).toBe("333-444-555-666-777");
  382. expect(div.textContent || div.innerText).toBe("444");
  383. });
  384. it("ReactDOM的回调总在最后",function() {
  385. var list = [];
  386. class App extends React.Component {
  387. constructor(props) {
  388. super(props);
  389. this.state = {
  390. path: "111"
  391. };
  392. }
  393. componentWillMount() {
  394. this.setState(
  395. {
  396. path: "222"
  397. },
  398. function() {
  399. list.push("componentWillMount cb");
  400. }
  401. );
  402. this.setState(
  403. {
  404. path: "2222"
  405. },
  406. function() {
  407. list.push("componentWillMount cb2");
  408. }
  409. );
  410. }
  411. render() {
  412. list.push("render " + this.state.path);
  413. return (
  414. <div>
  415. <span>
  416. {this.state.path}
  417. <Child parent={this} />
  418. </span>
  419. </div>
  420. );
  421. }
  422. componentDidMount() {
  423. this.setState(
  424. {
  425. path: "eeee"
  426. },
  427. function() {
  428. list.push("componentDidMount cb");
  429. }
  430. );
  431. }
  432. componentWillUpdate() {
  433. list.push("will update");
  434. }
  435. componentDidUpdate() {
  436. list.push("did update");
  437. }
  438. }
  439. class Child extends React.Component {
  440. componentWillMount() {
  441. this.props.parent.setState(
  442. {
  443. path: "child"
  444. },
  445. function() {
  446. list.push("child setState");
  447. }
  448. );
  449. }
  450. render() {
  451. list.push("child render");
  452. return <p>33333</p>;
  453. }
  454. }
  455. ReactDOM.render(<App />, div, function() {
  456. list.push("ReactDOM cb");
  457. });
  458. expect(list).toEqual([
  459. "render 2222",
  460. "child render",
  461. "will update",
  462. "render eeee",
  463. "child render",
  464. "did update",
  465. "componentWillMount cb",
  466. "componentWillMount cb2",
  467. "child setState",
  468. "componentDidMount cb",
  469. "ReactDOM cb"
  470. ]);
  471. });
  472. it("在componentWillUnmount中setState应该不起作用", function() {
  473. class Issue extends React.PureComponent {
  474. constructor(props) {
  475. super(props);
  476. this.state = {
  477. status: "normal"
  478. };
  479. }
  480. componentWillUnmount() {
  481. this.setState({ status: "unmount" });
  482. }
  483. render() {
  484. const { status } = this.state;
  485. if (status === "unmount") {
  486. throw new Error("Issue unmounted");
  487. }
  488. return <span>Issue status: {status}</span>;
  489. }
  490. }
  491. class App extends React.PureComponent {
  492. constructor(props) {
  493. super(props);
  494. this.state = {
  495. showIssue: true
  496. };
  497. }
  498. render() {
  499. const { showIssue } = this.state;
  500. return (
  501. <div>
  502. {showIssue && <Issue />}
  503. <button
  504. type="button"
  505. ref="button"
  506. onClick={() => this.setState({ showIssue: !showIssue })}
  507. >
  508. Click
  509. </button>
  510. </div>
  511. );
  512. }
  513. }
  514. var s = React.render(<App />, div);
  515. expect(div.getElementsByTagName("span").length).toBe(1);
  516. ReactTestUtils.Simulate.click(s.refs.button);
  517. expect(div.getElementsByTagName("span").length).toBe(0);
  518. });
  519. it("forceUpdate在componentDidMount中使用",function(){
  520. var list = [];
  521. class App extends React.Component {
  522. constructor(props) {
  523. super(props);
  524. this.state = {
  525. aaa: "aaa"
  526. };
  527. }
  528. componentWillMount() {
  529. this.setState(
  530. {
  531. aaa: "bbb"
  532. },
  533. function() {
  534. list.push("1111");
  535. }
  536. );
  537. }
  538. componentDidMount() {
  539. this.state.aaa = "cccc";
  540. this.forceUpdate(function() {
  541. list.push("2222");
  542. });
  543. this.state.aaa = "dddd";
  544. this.forceUpdate(function() {
  545. list.push("3333");
  546. });
  547. list.push("did mount");
  548. }
  549. componentWillUpdate() {
  550. list.push("app will update");
  551. }
  552. componentDidUpdate() {
  553. list.push("app did update");
  554. }
  555. render() {
  556. list.push("render " + this.state.aaa);
  557. return <div>{this.state.aaa}</div>;
  558. }
  559. }
  560. ReactDOM.render(<App />, div, function() {
  561. list.push("ReactDOM cb");
  562. });
  563. expect(list).toEqual([
  564. "render bbb",
  565. "did mount",
  566. "app will update",
  567. "render dddd",
  568. "app did update",
  569. "1111",
  570. "2222",
  571. "3333",
  572. "ReactDOM cb"
  573. ]);
  574. });
  575. /*it("事件回调里执行多个组件的setState,不会按触发时的顺序执行,而是按文档顺序执行",function(){
  576. var list = [];
  577. class App extends React.Component {
  578. constructor(props) {
  579. super(props);
  580. this.handleClick = this.handleClick.bind(this);
  581. }
  582. handleClick() {
  583. this.refs.c.setState({
  584. text:"第1"
  585. },function(){
  586. list.push("c的回调");
  587. });
  588. this.refs.a.setState({
  589. text:"第2"
  590. }, function(){
  591. list.push("a的回调");
  592. });
  593. this.refs.b.setState({
  594. text:"第3"
  595. },function(){
  596. list.push("b的回调");
  597. });
  598. }
  599. render() {
  600. return <div ref="kk" onClick={this.handleClick.bind(this)}>
  601. <Child name="a" ref="a" >aaa</Child>
  602. <Child name="b" ref="b" >bbb</Child>
  603. <Child name="c" ref="c" >ccc</Child>
  604. </div>;
  605. }
  606. }
  607. class Child extends React.Component {
  608. constructor(props) {
  609. super(props);
  610. this.state = {
  611. text: props.children
  612. };
  613. }
  614. componentWillUpdate(){
  615. list.push(this.props.name+" will update");
  616. }
  617. componentDidUpdate(){
  618. list.push(this.props.name+" did update");
  619. }
  620. render(){
  621. return <span className={this.props.name}>{this.state.text}</span>;
  622. }
  623. }
  624. var s = ReactDOM.render(<App />, div);
  625. ReactTestUtils.Simulate.click(s.refs.kk);
  626. expect(list).toEqual([
  627. "a will update",
  628. "b will update",
  629. "c will update",
  630. "a did update",
  631. "b did update",
  632. "c did update",
  633. "a的回调",
  634. "b的回调",
  635. "c的回调"
  636. ]);
  637. });*/
  638. });