Compare commits
839 Commits
v0.14.8
...
v15.0.0-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36796348f2 | ||
|
|
e61e6dd4ed | ||
|
|
340b344b20 | ||
|
|
dc93efaf11 | ||
|
|
56f8321559 | ||
|
|
f3738d2cd0 | ||
|
|
626bb81241 | ||
|
|
2b0f730081 | ||
|
|
6a5866f286 | ||
|
|
f430c47467 | ||
|
|
10f9476f3a | ||
|
|
9c1916dedb | ||
|
|
3b86cb10c4 | ||
|
|
4e2a0d14a9 | ||
|
|
1dc705aa0b | ||
|
|
9fff1693f6 | ||
|
|
8b22a82162 | ||
|
|
f707ee53e2 | ||
|
|
25c2bfcd52 | ||
|
|
36798f7395 | ||
|
|
0ba1073e17 | ||
|
|
8a9ab75261 | ||
|
|
b89e7d25d5 | ||
|
|
093bb22030 | ||
|
|
45a321d686 | ||
|
|
b271efb7d3 | ||
|
|
f8046f2dc2 | ||
|
|
18af9bc91e | ||
|
|
09666ad486 | ||
|
|
4bbd8d25e1 | ||
|
|
5eab1bbe29 | ||
|
|
32b0c62214 | ||
|
|
56c423afd6 | ||
|
|
4da7e7ef9c | ||
|
|
aec8a9b4fe | ||
|
|
e0442f6915 | ||
|
|
ab6041bad6 | ||
|
|
8f2b7d8e71 | ||
|
|
a026b35626 | ||
|
|
730e5ac2e6 | ||
|
|
03925f4f06 | ||
|
|
4045747af6 | ||
|
|
132a291747 | ||
|
|
96eb7ffdda | ||
|
|
7dc8910373 | ||
|
|
4d1bfcc8e1 | ||
|
|
5696ccfcd7 | ||
|
|
adc9104d84 | ||
|
|
f68325f5d7 | ||
|
|
5bb4303f9d | ||
|
|
1365498b81 | ||
|
|
4c804361e0 | ||
|
|
1e5bb2e9e4 | ||
|
|
471b4af2c8 | ||
|
|
13871886ba | ||
|
|
92530b4ddc | ||
|
|
74070e5838 | ||
|
|
5b13cbc0e8 | ||
|
|
895fab782b | ||
|
|
260353e977 | ||
|
|
8ab15c1d84 | ||
|
|
e2866ae412 | ||
|
|
040e94b7f5 | ||
|
|
dff05beeff | ||
|
|
7a3009c2b4 | ||
|
|
f7276b304c | ||
|
|
308badbc03 | ||
|
|
39c597f523 | ||
|
|
8886cee285 | ||
|
|
de09e0acd8 | ||
|
|
8011112cc1 | ||
|
|
fb75341769 | ||
|
|
5652c710c0 | ||
|
|
6ec8f078f1 | ||
|
|
01b3196896 | ||
|
|
5ebb784be5 | ||
|
|
ccfd1ffb30 | ||
|
|
cf4255445e | ||
|
|
70b5eda83e | ||
|
|
dcc86b63dc | ||
|
|
a080d863c5 | ||
|
|
628a16c84d | ||
|
|
b3335f371f | ||
|
|
559fed7988 | ||
|
|
98d3327a5d | ||
|
|
81e5dd0a29 | ||
|
|
e8fb8c7870 | ||
|
|
3863330524 | ||
|
|
684ef3e320 | ||
|
|
f818fa38b4 | ||
|
|
203850013a | ||
|
|
5d7cd93b0e | ||
|
|
efa8624eb9 | ||
|
|
decff26e81 | ||
|
|
6454bfb9de | ||
|
|
c54249c868 | ||
|
|
e8e56e8723 | ||
|
|
6312852688 | ||
|
|
51d16473b0 | ||
|
|
5879c9e1a4 | ||
|
|
8d08f3f00a | ||
|
|
3a4e1dbb5b | ||
|
|
5ffd1db570 | ||
|
|
62e9531023 | ||
|
|
acb49363d5 | ||
|
|
c9e0fc766f | ||
|
|
2fdaba49c7 | ||
|
|
50c28fa9c4 | ||
|
|
383e2f938d | ||
|
|
73ad44567c | ||
|
|
356fa4e8c0 | ||
|
|
d26ec08cf1 | ||
|
|
41dea65abf | ||
|
|
3bee2d962a | ||
|
|
c3980a6d08 | ||
|
|
ee64241e90 | ||
|
|
5b80f9009f | ||
|
|
ec41af0557 | ||
|
|
2410dc7f15 | ||
|
|
57d59ea344 | ||
|
|
8aacf1e850 | ||
|
|
175d0978cc | ||
|
|
0b87d7f56f | ||
|
|
17c9dffed4 | ||
|
|
d684b1598d | ||
|
|
b3eaab9f75 | ||
|
|
5ce1fea75a | ||
|
|
385cadf8e3 | ||
|
|
c4a2425eca | ||
|
|
bbd5a78efa | ||
|
|
dd390b3e36 | ||
|
|
293dc75584 | ||
|
|
428ef03bbb | ||
|
|
3e41da767f | ||
|
|
b560fea08d | ||
|
|
e5b304ebe3 | ||
|
|
fcfbcf79a0 | ||
|
|
3ce7e91946 | ||
|
|
bd3979980f | ||
|
|
6fe495a5ab | ||
|
|
0509950e74 | ||
|
|
48ded230fc | ||
|
|
4a1b0b7dfa | ||
|
|
9d22ce8440 | ||
|
|
08c7b1aa40 | ||
|
|
1e85460909 | ||
|
|
38bf3583cb | ||
|
|
a90344a9c1 | ||
|
|
725a723e27 | ||
|
|
e4b463f921 | ||
|
|
886044312c | ||
|
|
a1dab9edbf | ||
|
|
81e41ae1b1 | ||
|
|
86305fba41 | ||
|
|
220b4b6b50 | ||
|
|
b38b39a696 | ||
|
|
97e0fe5d91 | ||
|
|
dbb54b1990 | ||
|
|
f2bb01506a | ||
|
|
ef2b1f26ef | ||
|
|
8c7d743361 | ||
|
|
292f2b7608 | ||
|
|
0bd65aa028 | ||
|
|
2981bef075 | ||
|
|
1beae0c4ff | ||
|
|
823966ef33 | ||
|
|
d136d28f55 | ||
|
|
1ccec8b711 | ||
|
|
cded0c5c23 | ||
|
|
188e8cde2f | ||
|
|
9d5825cf4b | ||
|
|
67e1291ef7 | ||
|
|
a0c32cb629 | ||
|
|
9c3f595597 | ||
|
|
6986fdd6bd | ||
|
|
c569b329e9 | ||
|
|
6ecf06ce60 | ||
|
|
e9636b1f6a | ||
|
|
4b2b7b0d3e | ||
|
|
fc547e8380 | ||
|
|
01f10eb7ba | ||
|
|
ed40119db8 | ||
|
|
a92bc51e0a | ||
|
|
6f5e619fae | ||
|
|
edaf5b43b4 | ||
|
|
ea79138156 | ||
|
|
0ce28c6e88 | ||
|
|
e1b7c3aa0c | ||
|
|
096115c5c9 | ||
|
|
080ff5ad73 | ||
|
|
7c52b802b4 | ||
|
|
4c03bafa4e | ||
|
|
1aa7fd0d24 | ||
|
|
7e779a9d44 | ||
|
|
b41f5a1cdf | ||
|
|
30f7641873 | ||
|
|
76da1f8599 | ||
|
|
757756f682 | ||
|
|
e9e70365c3 | ||
|
|
af43d06e97 | ||
|
|
7f98dc897d | ||
|
|
e420edda36 | ||
|
|
3da6e2889b | ||
|
|
7cee5022f8 | ||
|
|
49fa7d139d | ||
|
|
31d3bfa763 | ||
|
|
ae5ff24da6 | ||
|
|
c7808cac69 | ||
|
|
f5a9a26a43 | ||
|
|
e09dfe1e0e | ||
|
|
1186cb9ab4 | ||
|
|
7a6000c093 | ||
|
|
aedfa3011e | ||
|
|
2f792d5a60 | ||
|
|
accd0d874d | ||
|
|
422b4e105c | ||
|
|
909cba2254 | ||
|
|
209b7522d9 | ||
|
|
be0551d01e | ||
|
|
f1c1544401 | ||
|
|
fb5cd2f7ae | ||
|
|
fcc63e1fff | ||
|
|
7334fece2c | ||
|
|
689efd1abf | ||
|
|
0acdb7b56a | ||
|
|
1da992a0ab | ||
|
|
ad4e31c312 | ||
|
|
fdd7fabd12 | ||
|
|
9494ec80c2 | ||
|
|
dd93eb7f40 | ||
|
|
1b3adff1ca | ||
|
|
da1fcd0fbf | ||
|
|
70097ea27c | ||
|
|
6f976dfbbe | ||
|
|
2e1fb4b529 | ||
|
|
e8af100849 | ||
|
|
ee03c19b3b | ||
|
|
b60e8c255d | ||
|
|
26f53de4a6 | ||
|
|
171305f7e2 | ||
|
|
f2b62e9d90 | ||
|
|
d51a1d4ec1 | ||
|
|
f7850dd3d7 | ||
|
|
67e85df1fa | ||
|
|
77a137adf9 | ||
|
|
ea0ac61945 | ||
|
|
708e1b3f03 | ||
|
|
0c6b909322 | ||
|
|
3574e60867 | ||
|
|
23167f287e | ||
|
|
a3ec1c34d6 | ||
|
|
a1584053cc | ||
|
|
31fc7fd627 | ||
|
|
c3cea365e7 | ||
|
|
70938de9ae | ||
|
|
a7f7ef6ac3 | ||
|
|
5597873d7c | ||
|
|
19db8e13bc | ||
|
|
78be6f45cc | ||
|
|
d3e315193a | ||
|
|
9b679ab56a | ||
|
|
f4c5b2c00b | ||
|
|
2751e8179d | ||
|
|
4e3d38ed27 | ||
|
|
0e8db6ba42 | ||
|
|
eb00290673 | ||
|
|
27926572f6 | ||
|
|
0ebc7b60e1 | ||
|
|
067547c1d1 | ||
|
|
7f322b5a46 | ||
|
|
102cd29189 | ||
|
|
a8c2c80fdc | ||
|
|
bdcb69f43d | ||
|
|
c035179ef8 | ||
|
|
3b96650e39 | ||
|
|
2a042a3060 | ||
|
|
e15e059d73 | ||
|
|
dd2dbd0a5c | ||
|
|
6dc7f13343 | ||
|
|
2a64098271 | ||
|
|
7cc6738061 | ||
|
|
8f720f8cd9 | ||
|
|
a58cde6c79 | ||
|
|
53920e7c38 | ||
|
|
99dc2a73f6 | ||
|
|
62c9d999be | ||
|
|
c29642d6ed | ||
|
|
bef45b0b1a | ||
|
|
51295bf7d0 | ||
|
|
a0d6a98cbf | ||
|
|
713401f2eb | ||
|
|
feb4c487be | ||
|
|
53dabe748c | ||
|
|
98a7100930 | ||
|
|
f27e3aa750 | ||
|
|
251d6c30b5 | ||
|
|
232a47ad04 | ||
|
|
6e0a023572 | ||
|
|
badc15e3c0 | ||
|
|
82fe64a456 | ||
|
|
edf1952421 | ||
|
|
432578ef03 | ||
|
|
5043b89d5e | ||
|
|
07c0bc6166 | ||
|
|
d9dabac48d | ||
|
|
0d5312559a | ||
|
|
a0741ab468 | ||
|
|
56365f4eda | ||
|
|
6ff96441da | ||
|
|
35e1908bb4 | ||
|
|
f0a15b993c | ||
|
|
4865ddf7ea | ||
|
|
109c9a91e2 | ||
|
|
bae0f19543 | ||
|
|
bb11639e25 | ||
|
|
89a0ef4ff9 | ||
|
|
85e646015d | ||
|
|
1d8b816496 | ||
|
|
8fa1dfaa8c | ||
|
|
39c24bf6a6 | ||
|
|
e68e17503e | ||
|
|
f3dbc40261 | ||
|
|
bbef2958b2 | ||
|
|
5b98152a3d | ||
|
|
a9a4c9d9d0 | ||
|
|
9b0ef9244d | ||
|
|
82ed8fb0cd | ||
|
|
7e2a7f0ce6 | ||
|
|
88ce0fc95f | ||
|
|
c465c2ab77 | ||
|
|
744b1175f3 | ||
|
|
14d8593102 | ||
|
|
d872c25739 | ||
|
|
ac20cb2d8c | ||
|
|
9c57c30049 | ||
|
|
cd0c62ef74 | ||
|
|
50af034108 | ||
|
|
dae1152e66 | ||
|
|
ca31e9f3f7 | ||
|
|
dc9b3fedb2 | ||
|
|
8d0efaf980 | ||
|
|
a8b953327a | ||
|
|
a067949c99 | ||
|
|
aad8024d09 | ||
|
|
1b85c9a6c4 | ||
|
|
abddb558ec | ||
|
|
7c0189fbad | ||
|
|
89538d44a9 | ||
|
|
421d177dd2 | ||
|
|
5563771ace | ||
|
|
40d5692980 | ||
|
|
bdca170d2a | ||
|
|
9a6c5deeb6 | ||
|
|
f56b28c07a | ||
|
|
7a1d87bd33 | ||
|
|
8eabf84204 | ||
|
|
a3d6553a30 | ||
|
|
26f3785a8c | ||
|
|
83328d4923 | ||
|
|
b8315fdd59 | ||
|
|
963b3cacab | ||
|
|
c9c3c339b7 | ||
|
|
ab37776cc2 | ||
|
|
0c15b01c38 | ||
|
|
3afced6a75 | ||
|
|
a15293704a | ||
|
|
ccb97d8e59 | ||
|
|
20e5a95fe3 | ||
|
|
bec238a7c9 | ||
|
|
6c8cbcae8f | ||
|
|
13080f99ba | ||
|
|
30ef056731 | ||
|
|
a55ab282fa | ||
|
|
33217f0fb9 | ||
|
|
3997164418 | ||
|
|
eee5d466a6 | ||
|
|
b6a01d7733 | ||
|
|
2ff1b2e9e3 | ||
|
|
db37deb17c | ||
|
|
55bd203310 | ||
|
|
83ee3c38a4 | ||
|
|
682d61537d | ||
|
|
0886273438 | ||
|
|
410cc30bf9 | ||
|
|
fcf74fe9da | ||
|
|
f282710072 | ||
|
|
ef32927eb5 | ||
|
|
eeee272268 | ||
|
|
94325ea3ef | ||
|
|
bb084eba24 | ||
|
|
b1964ad8a7 | ||
|
|
4f7a2766e4 | ||
|
|
46f5251c66 | ||
|
|
be964561ed | ||
|
|
3deca65a89 | ||
|
|
0130a04f05 | ||
|
|
c65ff201fc | ||
|
|
86b44349de | ||
|
|
178fe648de | ||
|
|
14725ba768 | ||
|
|
1d3ff2a159 | ||
|
|
856eb72b4f | ||
|
|
b15c80696c | ||
|
|
3faf9569d4 | ||
|
|
67663ba2c9 | ||
|
|
4f9d8c31b8 | ||
|
|
2f77367863 | ||
|
|
4a971c2d1c | ||
|
|
c77f0b2fc5 | ||
|
|
ad54e49f0c | ||
|
|
7d699f4063 | ||
|
|
f4744f3a70 | ||
|
|
8c7603311a | ||
|
|
526d5c0edc | ||
|
|
31253c251f | ||
|
|
50c7b1792c | ||
|
|
0be7786e1c | ||
|
|
3ca15b0090 | ||
|
|
b0ae955977 | ||
|
|
8557cc0d67 | ||
|
|
9c21e2f3c4 | ||
|
|
6b3ee0e74d | ||
|
|
a0f88d29df | ||
|
|
f5840e685e | ||
|
|
36dfe62f68 | ||
|
|
13c398bef6 | ||
|
|
25ef456b13 | ||
|
|
fbf81a8435 | ||
|
|
940a75b1fe | ||
|
|
f1d3f78334 | ||
|
|
34fbcf20d8 | ||
|
|
f48bb3a274 | ||
|
|
6b641de6b8 | ||
|
|
d54b151bc2 | ||
|
|
1e98c78541 | ||
|
|
ed8727ade7 | ||
|
|
50c81d5fe6 | ||
|
|
ba0792e72b | ||
|
|
8248470bd9 | ||
|
|
418ba27485 | ||
|
|
08499f9938 | ||
|
|
3722616349 | ||
|
|
c6025da952 | ||
|
|
54e6057b1a | ||
|
|
e95d3dd812 | ||
|
|
58901af7a6 | ||
|
|
18a3a3979e | ||
|
|
e02bd0543f | ||
|
|
f44a0778e3 | ||
|
|
d01188133e | ||
|
|
b74e53c3ca | ||
|
|
7ea1d15197 | ||
|
|
bf4bd19101 | ||
|
|
60af7d1c3d | ||
|
|
c5867ea401 | ||
|
|
812e1a877f | ||
|
|
64f795e5ef | ||
|
|
23edc0d274 | ||
|
|
acabb22a1d | ||
|
|
907dee2b5d | ||
|
|
1cdbff26ab | ||
|
|
aa1e58a41b | ||
|
|
179263c72f | ||
|
|
22a3f6724a | ||
|
|
1d071c20e8 | ||
|
|
c643ecd7c7 | ||
|
|
ab6892956d | ||
|
|
0440de9f89 | ||
|
|
399a6cfd1c | ||
|
|
b40221973c | ||
|
|
fd03270372 | ||
|
|
844ca8b6b2 | ||
|
|
d1eba1f78c | ||
|
|
5a80b20017 | ||
|
|
a3779421a1 | ||
|
|
1a6d1e74e0 | ||
|
|
e7a5a98044 | ||
|
|
904e9e3ea6 | ||
|
|
3812b95450 | ||
|
|
c4f134883d | ||
|
|
60cba8fcf7 | ||
|
|
7729d51726 | ||
|
|
c07b304c76 | ||
|
|
538d0b08f2 | ||
|
|
e03df26e62 | ||
|
|
6c89857cdc | ||
|
|
892352e95d | ||
|
|
ba8c987391 | ||
|
|
85a3142f04 | ||
|
|
80bcc519d7 | ||
|
|
94504c3767 | ||
|
|
7a453646be | ||
|
|
96549a1f48 | ||
|
|
034cfc96c7 | ||
|
|
72d9a8bc15 | ||
|
|
09b7ff1fc4 | ||
|
|
3bcb0d63f0 | ||
|
|
dbe8212974 | ||
|
|
593a234c1e | ||
|
|
2d5612913f | ||
|
|
a2b471e098 | ||
|
|
47253733d3 | ||
|
|
ea31f924b8 | ||
|
|
8104262043 | ||
|
|
951f3b6ba8 | ||
|
|
12a20b184c | ||
|
|
358140679c | ||
|
|
1220ccc805 | ||
|
|
fc245226f9 | ||
|
|
4cb210a9da | ||
|
|
7d0ee24514 | ||
|
|
575d1a5093 | ||
|
|
eecd2953cd | ||
|
|
e39f51429b | ||
|
|
d6a547f793 | ||
|
|
30ca6619dc | ||
|
|
b4b1adddda | ||
|
|
4b80ea23a1 | ||
|
|
f9a8bdcafe | ||
|
|
586653935c | ||
|
|
10edb83166 | ||
|
|
95da62765e | ||
|
|
5bdb808a46 | ||
|
|
a88c39be86 | ||
|
|
bc0dc86ae6 | ||
|
|
c8b8c818b5 | ||
|
|
240d7c82cc | ||
|
|
99fbde54a1 | ||
|
|
00eddc2060 | ||
|
|
2f7f1a9d45 | ||
|
|
5fec308d60 | ||
|
|
d16754125f | ||
|
|
59dd7b33ee | ||
|
|
73b496db95 | ||
|
|
22b8952fbf | ||
|
|
aca4ccda35 | ||
|
|
6d5fe44c86 | ||
|
|
88584a96b7 | ||
|
|
2f6fb746b9 | ||
|
|
a86d25d584 | ||
|
|
9b7cc43330 | ||
|
|
b9fd77a4bd | ||
|
|
4a7456a136 | ||
|
|
8987522a2a | ||
|
|
e131357c23 | ||
|
|
9419976e51 | ||
|
|
578312e267 | ||
|
|
76bbd9e660 | ||
|
|
e2fdcb1990 | ||
|
|
cd7387c465 | ||
|
|
80af7c7be4 | ||
|
|
4fae036269 | ||
|
|
1c14477a47 | ||
|
|
7ae67916cc | ||
|
|
35962a0008 | ||
|
|
c193d1a0c2 | ||
|
|
b80f676d27 | ||
|
|
35543c6312 | ||
|
|
3cebadaa6b | ||
|
|
e48c8be556 | ||
|
|
f06ddf7905 | ||
|
|
663c4b7d2e | ||
|
|
5d94d7d43c | ||
|
|
4ba0e95a96 | ||
|
|
796f8c353c | ||
|
|
d11ccd6956 | ||
|
|
f470cb88f8 | ||
|
|
6d20556c78 | ||
|
|
ce528457a4 | ||
|
|
5a7c273a1b | ||
|
|
276ef737fe | ||
|
|
5becbe3f08 | ||
|
|
19e2cf5ad5 | ||
|
|
45c612ed7a | ||
|
|
84af306044 | ||
|
|
2858bca2e3 | ||
|
|
9ad4be0316 | ||
|
|
a4b5f2fffa | ||
|
|
c802e9ede9 | ||
|
|
d2888798f0 | ||
|
|
17289020f7 | ||
|
|
5724fca7ae | ||
|
|
fbb8d2fe2d | ||
|
|
b9371bc216 | ||
|
|
2b54e5417d | ||
|
|
bc3b32c968 | ||
|
|
db7ea058e7 | ||
|
|
dbab71bbf4 | ||
|
|
ff6714064f | ||
|
|
72a11421d2 | ||
|
|
a82400faa2 | ||
|
|
d44c45f8a5 | ||
|
|
3c1c02394e | ||
|
|
e9f931653d | ||
|
|
57b0c8efd0 | ||
|
|
6e1f642273 | ||
|
|
88bae3fb73 | ||
|
|
41fc5f21e5 | ||
|
|
bbffc36534 | ||
|
|
8a9039891b | ||
|
|
c525732147 | ||
|
|
b5d90f8c6b | ||
|
|
d77e161f99 | ||
|
|
a2d26c82ea | ||
|
|
0c790baf31 | ||
|
|
68eb1a6fb6 | ||
|
|
86fc94750a | ||
|
|
bd6417e24e | ||
|
|
3d75a4234d | ||
|
|
6fd7892a4d | ||
|
|
38b80b5b37 | ||
|
|
761921499c | ||
|
|
aa6fcf902c | ||
|
|
c35b4384f7 | ||
|
|
f02584d807 | ||
|
|
0ef5112e89 | ||
|
|
d914f15c0e | ||
|
|
1384d43f53 | ||
|
|
d80710b0ae | ||
|
|
d15e2fcd8e | ||
|
|
cc09ed940f | ||
|
|
12c214a992 | ||
|
|
f00d45d65f | ||
|
|
ac17bdaaba | ||
|
|
606aa61209 | ||
|
|
29523d0113 | ||
|
|
d95a2239a8 | ||
|
|
b8f502b224 | ||
|
|
c0f0d12f96 | ||
|
|
2ecfc4f8fb | ||
|
|
9d25303d89 | ||
|
|
c341269ab2 | ||
|
|
01341480c2 | ||
|
|
fbf4cbbae6 | ||
|
|
2257d8aa76 | ||
|
|
afc118e3c4 | ||
|
|
ee06df1ac5 | ||
|
|
a65a4851c6 | ||
|
|
e849819cb6 | ||
|
|
2c176da7e8 | ||
|
|
f8b4affb87 | ||
|
|
ad00a6e672 | ||
|
|
b0a7a00dba | ||
|
|
7f205e9c1b | ||
|
|
65b9ceb4f8 | ||
|
|
517872817f | ||
|
|
ad654ceb66 | ||
|
|
e26666fb1e | ||
|
|
59db07c4f9 | ||
|
|
e603644255 | ||
|
|
f558b95584 | ||
|
|
0f76873ebb | ||
|
|
e2267613ac | ||
|
|
924cddc505 | ||
|
|
a51ef94384 | ||
|
|
055ecd7f14 | ||
|
|
d17ad0f0c0 | ||
|
|
0508a07446 | ||
|
|
0ddc0817e4 | ||
|
|
11919b6d90 | ||
|
|
73ec3713b3 | ||
|
|
22a240fd39 | ||
|
|
56c91248fd | ||
|
|
2f48cc4327 | ||
|
|
75e9045719 | ||
|
|
2b136a4f84 | ||
|
|
6269ef62cf | ||
|
|
8aaa66c292 | ||
|
|
3a0ab000bf | ||
|
|
4106251316 | ||
|
|
939c7596ad | ||
|
|
b447b40958 | ||
|
|
7221e1b2e9 | ||
|
|
263ca4792e | ||
|
|
4d41cf740a | ||
|
|
a79fcd7a7f | ||
|
|
be6bd3404e | ||
|
|
bb3a326ecd | ||
|
|
b106e96864 | ||
|
|
7b97d8d58f | ||
|
|
1bcd6804f3 | ||
|
|
1dafac9e0c | ||
|
|
85fc28624c | ||
|
|
29939b7270 | ||
|
|
ce002e61b1 | ||
|
|
a468eed33f | ||
|
|
8848984c46 | ||
|
|
caece8b4b7 | ||
|
|
5bd04cf259 | ||
|
|
fc96f31fad | ||
|
|
244dd5da4c | ||
|
|
7aac368253 | ||
|
|
3d7def0b48 | ||
|
|
545f31741e | ||
|
|
8f1ce99cc3 | ||
|
|
2a7d054bfc | ||
|
|
db7522f2c5 | ||
|
|
9cb01de1bc | ||
|
|
1044d4c7d5 | ||
|
|
dd3c4474d1 | ||
|
|
617f03518a | ||
|
|
7a164fd3a0 | ||
|
|
278939ec58 | ||
|
|
cdaea311a3 | ||
|
|
98c96b6460 | ||
|
|
e0904a0a90 | ||
|
|
3260b00385 | ||
|
|
e67ceae1ad | ||
|
|
f7816cdbfe | ||
|
|
0fc8f6ade3 | ||
|
|
3d4e8affcd | ||
|
|
20ec78d9f0 | ||
|
|
b5e7a8447c | ||
|
|
ef95128ae2 | ||
|
|
ed7ab7b94a | ||
|
|
e12ee95e09 | ||
|
|
5ceb22953c | ||
|
|
ccfc2d8049 | ||
|
|
95afe4608f | ||
|
|
0dc6596dc2 | ||
|
|
b735dd4ab4 | ||
|
|
b42c1dafcc | ||
|
|
5ae4f035c7 | ||
|
|
8168c8eac6 | ||
|
|
c47d2cfada | ||
|
|
8e9682c542 | ||
|
|
5f8fe7d614 | ||
|
|
8e67b7f624 | ||
|
|
5c5d2ec182 | ||
|
|
659172df50 | ||
|
|
555fd46c7e | ||
|
|
a299a366ce | ||
|
|
da1135618e | ||
|
|
4f9fdeeec1 | ||
|
|
1206d8d2eb | ||
|
|
e89e675508 | ||
|
|
af99b2c2a3 | ||
|
|
dc21b95244 | ||
|
|
dddbca472a | ||
|
|
622db4ee4f | ||
|
|
194ab16d7b | ||
|
|
51c0f86ec7 | ||
|
|
6488a4c187 | ||
|
|
50c9b94552 | ||
|
|
4a37796f88 | ||
|
|
f7a3ac2d56 | ||
|
|
2c1c7fad4d | ||
|
|
bffed177f4 | ||
|
|
e46e5784ce | ||
|
|
c1c9b383a0 | ||
|
|
b4a6bc89c2 | ||
|
|
b32835ea61 | ||
|
|
db989bf686 | ||
|
|
37c71a6e1f | ||
|
|
48ada00bdb | ||
|
|
abaf0051de | ||
|
|
45d4296ac5 | ||
|
|
875e2b36cc | ||
|
|
b03b195759 | ||
|
|
227ba42f81 | ||
|
|
e45e46a567 | ||
|
|
0e451162bc | ||
|
|
f8f2be05ce | ||
|
|
01817c143c | ||
|
|
dd0b1a643b | ||
|
|
5dc2858bc9 | ||
|
|
0b21632f8e | ||
|
|
a86e349535 | ||
|
|
2b81ea0d20 | ||
|
|
0b29035484 | ||
|
|
ab1be09d7d | ||
|
|
be90351add | ||
|
|
5c3e99c7f4 | ||
|
|
f6e09f1903 | ||
|
|
fb9e1ed475 | ||
|
|
8210299437 | ||
|
|
1dca72ebfa | ||
|
|
a907da9430 | ||
|
|
4e48c67120 | ||
|
|
28b10a9d6a | ||
|
|
09b688efce | ||
|
|
15c731edca | ||
|
|
d95381387e | ||
|
|
9c836b09f2 | ||
|
|
8f4c2997a0 | ||
|
|
845135531a | ||
|
|
3359e2f64c | ||
|
|
e8be7013ba | ||
|
|
a8459214d1 | ||
|
|
72e70f345b | ||
|
|
407e88d6c8 | ||
|
|
98aecac34c | ||
|
|
1a7c0a4b16 | ||
|
|
ff17bc25d1 | ||
|
|
d96f319d6e | ||
|
|
f522371cee | ||
|
|
d4420eca8a | ||
|
|
3f5747009d | ||
|
|
c511f161df | ||
|
|
f3a506bc0c | ||
|
|
8fb436a5eb | ||
|
|
2253405dcc | ||
|
|
4fb39ce984 | ||
|
|
4bb83d237e | ||
|
|
e9796cc447 | ||
|
|
3fb2d3f5d4 | ||
|
|
2637a8eaf4 | ||
|
|
a7189c57fc | ||
|
|
9af15d3ecd | ||
|
|
0d12643652 | ||
|
|
319b374097 | ||
|
|
d0970ee8b8 | ||
|
|
f97d759db1 | ||
|
|
40cce38330 | ||
|
|
f31a46cb18 | ||
|
|
34d84a36d6 | ||
|
|
8ce7b7120c | ||
|
|
fe9a76ef25 | ||
|
|
60491d89f8 | ||
|
|
743ccf090f | ||
|
|
e4a43389b8 | ||
|
|
8ebbb7802c | ||
|
|
501a2765ac | ||
|
|
4a29fd892a | ||
|
|
e1d4668fd5 | ||
|
|
926f372dc5 | ||
|
|
30386b2e86 | ||
|
|
42481fe51f | ||
|
|
c2ae332718 | ||
|
|
59cd224a03 | ||
|
|
a69f05c881 | ||
|
|
fc043bb3a5 | ||
|
|
c103c8d065 | ||
|
|
3b28c72142 | ||
|
|
107e3014ef | ||
|
|
e65fe182e7 | ||
|
|
241533782c | ||
|
|
b7bf1ccafe | ||
|
|
8da4efa899 |
25
.babelrc
Normal file
25
.babelrc
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"presets": ["react"],
|
||||
"ignore": ["third_party"],
|
||||
"plugins": [
|
||||
"fbjs-scripts/babel-6/dev-expression",
|
||||
"syntax-trailing-function-commas",
|
||||
"transform-es2015-template-literals",
|
||||
"transform-es2015-literals",
|
||||
"transform-es2015-arrow-functions",
|
||||
"transform-es2015-block-scoped-functions",
|
||||
["transform-es2015-classes", { "loose": true }],
|
||||
"transform-es2015-object-super",
|
||||
"transform-es2015-shorthand-properties",
|
||||
"transform-es2015-computed-properties",
|
||||
"transform-es2015-for-of",
|
||||
"check-es2015-constants",
|
||||
["transform-es2015-spread", { "loose": true }],
|
||||
"transform-es2015-parameters",
|
||||
["transform-es2015-destructuring", { "loose": true }],
|
||||
"transform-es2015-block-scoping",
|
||||
"transform-es2015-modules-commonjs",
|
||||
"transform-es3-member-expression-literals",
|
||||
"transform-es3-property-literals"
|
||||
]
|
||||
}
|
||||
@@ -11,9 +11,6 @@ docs/vendor/bundle/
|
||||
examples/
|
||||
# Ignore built files.
|
||||
build/
|
||||
# react-codemod
|
||||
packages/react-codemod/test/
|
||||
packages/react-codemod/scripts/
|
||||
packages/react-codemod/build/
|
||||
packages/react-codemod/node_modules/
|
||||
coverage/
|
||||
scripts/bench/bench-*.js
|
||||
vendor/*
|
||||
|
||||
63
.eslintrc
63
.eslintrc
@@ -1,63 +0,0 @@
|
||||
---
|
||||
parser: babel-eslint
|
||||
|
||||
extends:
|
||||
- ./node_modules/fbjs-scripts/eslint/.eslintrc
|
||||
|
||||
plugins:
|
||||
- react
|
||||
- react-internal
|
||||
|
||||
# We're stricter than the default config, mostly. We'll override a few rules and
|
||||
# then enable some React specific ones.
|
||||
rules:
|
||||
accessor-pairs: 0
|
||||
brace-style: [2, 1tbs]
|
||||
comma-dangle: [2, always-multiline]
|
||||
consistent-return: 2
|
||||
dot-location: [2, property]
|
||||
dot-notation: 2
|
||||
eol-last: 2
|
||||
indent: [2, 2, {SwitchCase: 1}]
|
||||
jsx-quotes: [2, prefer-double]
|
||||
no-bitwise: 0
|
||||
no-dupe-class-members: 2
|
||||
no-multi-spaces: 2
|
||||
no-restricted-syntax: [2, WithStatement]
|
||||
no-shadow: 2
|
||||
no-unused-expressions: 2
|
||||
no-unused-vars: [2, {args: none}]
|
||||
quotes: [2, single, avoid-escape]
|
||||
space-after-keywords: 2
|
||||
space-before-blocks: 2
|
||||
# TODO: enable this rule after https://github.com/eslint/eslint/pull/3768 lands
|
||||
space-before-keywords: 0
|
||||
strict: [2, global]
|
||||
|
||||
# JSX
|
||||
# Our transforms set this automatically
|
||||
react/display-name: 0
|
||||
react/jsx-boolean-value: [2, always]
|
||||
react/jsx-no-undef: 2
|
||||
# We don't care to do this
|
||||
react/jsx-sort-prop-types: 0
|
||||
react/jsx-sort-props: 0
|
||||
react/jsx-uses-react: 2
|
||||
react/jsx-uses-vars: 2
|
||||
# It's easier to test some things this way
|
||||
react/no-did-mount-set-state: 0
|
||||
react/no-did-update-set-state: 0
|
||||
# We define multiple components in test files
|
||||
react/no-multi-comp: 0
|
||||
react/no-unknown-property: 2
|
||||
# This isn't useful in our test code
|
||||
react/prop-types: 0
|
||||
react/react-in-jsx-scope: 2
|
||||
react/self-closing-comp: 2
|
||||
# We don't care to do this
|
||||
react/sort-comp: 0
|
||||
react/wrap-multilines: [2, {declaration: false, assignment: false}]
|
||||
|
||||
# CUSTOM RULES
|
||||
# the second argument of warning/invariant should be a literal string
|
||||
react-internal/warning-and-invariant-args: 2
|
||||
73
.eslintrc.js
Normal file
73
.eslintrc.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const OFF = 0;
|
||||
const WARNING = 1;
|
||||
const ERROR = 2;
|
||||
|
||||
module.exports = {
|
||||
parser: 'babel-eslint',
|
||||
|
||||
extends: './node_modules/fbjs-scripts/eslint/.eslintrc.js',
|
||||
|
||||
plugins: [
|
||||
'react',
|
||||
'react-internal',
|
||||
],
|
||||
|
||||
ecmaFeatures: {
|
||||
modules: false
|
||||
},
|
||||
|
||||
// We're stricter than the default config, mostly. We'll override a few rules
|
||||
// and then enable some React specific ones.
|
||||
rules: {
|
||||
'accessor-pairs': OFF,
|
||||
'brace-style': [ERROR, '1tbs'],
|
||||
'comma-dangle': [ERROR, 'always-multiline'],
|
||||
'consistent-return': ERROR,
|
||||
'dot-location': [ERROR, 'property'],
|
||||
'dot-notation': ERROR,
|
||||
'eol-last': ERROR,
|
||||
'eqeqeq': [ERROR, 'allow-null'],
|
||||
'indent': [ERROR, 2, {SwitchCase: 1}],
|
||||
'jsx-quotes': [ERROR, 'prefer-double'],
|
||||
'no-bitwise': OFF,
|
||||
'no-multi-spaces': ERROR,
|
||||
'no-restricted-syntax': [ERROR, 'WithStatement'],
|
||||
'no-shadow': ERROR,
|
||||
'no-unused-expressions': ERROR,
|
||||
'no-unused-vars': [ERROR, {args: 'none'}],
|
||||
'quotes': [ERROR, 'single', 'avoid-escape'],
|
||||
'space-after-keywords': ERROR,
|
||||
'space-before-blocks': ERROR,
|
||||
'space-before-function-paren': [ERROR, {anonymous: 'never', named: 'never'}],
|
||||
'space-before-keywords': ERROR,
|
||||
'strict': [ERROR, 'global'],
|
||||
|
||||
// React & JSX
|
||||
// Our transforms set this automatically
|
||||
'react/display-name': OFF,
|
||||
'react/jsx-boolean-value': [ERROR, 'always'],
|
||||
'react/jsx-no-undef': ERROR,
|
||||
// We don't care to do this
|
||||
'react/jsx-sort-prop-types': OFF,
|
||||
'react/jsx-sort-props': OFF,
|
||||
'react/jsx-uses-react': ERROR,
|
||||
'react/jsx-uses-vars': ERROR,
|
||||
// It's easier to test some things this way
|
||||
'react/no-did-mount-set-state': OFF,
|
||||
'react/no-did-update-set-state': OFF,
|
||||
// We define multiple components in test files
|
||||
'react/no-multi-comp': OFF,
|
||||
'react/no-unknown-property': OFF,
|
||||
// This isn't useful in our test code
|
||||
'react/prop-types': OFF,
|
||||
'react/react-in-jsx-scope': ERROR,
|
||||
'react/self-closing-comp': ERROR,
|
||||
// We don't care to do this
|
||||
'react/sort-comp': OFF,
|
||||
'react/wrap-multilines': [ERROR, {declaration: false, assignment: false}],
|
||||
|
||||
// CUSTOM RULES
|
||||
// the second argument of warning/invariant should be a literal string
|
||||
'react-internal/warning-and-invariant-args': ERROR,
|
||||
}
|
||||
};
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@ static
|
||||
_SpecRunner.html
|
||||
__benchmarks__
|
||||
build/
|
||||
coverage/
|
||||
.module-cache
|
||||
*.gem
|
||||
docs/.bundle
|
||||
|
||||
19
.travis.yml
19
.travis.yml
@@ -65,13 +65,26 @@ script:
|
||||
-F "branch=$TRAVIS_BRANCH" \
|
||||
$SERVER
|
||||
fi
|
||||
elif [ "$TEST_TYPE" = test ]; then
|
||||
if [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
set -e
|
||||
./node_modules/.bin/grunt jest:coverage
|
||||
cat ./coverage/lcov.info | ./node_modules/.bin/coveralls
|
||||
else
|
||||
./node_modules/.bin/grunt jest:normal
|
||||
fi
|
||||
echo 'Testing in server-render (HTML generation) mode...'
|
||||
printf '\nmodule.exports.useCreateElement = false;\n' \
|
||||
>> src/renderers/dom/shared/ReactDOMFeatureFlags.js
|
||||
./node_modules/.bin/grunt jest:normal
|
||||
git checkout -- src/renderers/dom/shared/ReactDOMFeatureFlags.js
|
||||
else
|
||||
./node_modules/.bin/grunt $TEST_TYPE
|
||||
fi
|
||||
env:
|
||||
matrix:
|
||||
- TEST_TYPE=build
|
||||
- TEST_TYPE=jest
|
||||
- TEST_TYPE=test
|
||||
- TEST_TYPE=lint
|
||||
- TEST_TYPE=build_website
|
||||
global:
|
||||
@@ -81,8 +94,8 @@ env:
|
||||
- secure: dkpPW+VnoqC/okhRdV90m36NcyBFhcwEKL3bNFExAwi0dXnFao8RoFlvnwiPlA23h2faROkMIetXlti6Aju08BgUFV+f9aL6vLyU7gUent4Nd3413zf2fwDtXIWIETg6uLnOpSykGKgCAT/hY3Q2oPLqOoY0OxfgnbqwxkxljrE=
|
||||
# GITHUB_TOKEN
|
||||
- secure: EHCyCSKMwKlLHNtcj9nmkRzmiiPE3aDGlPcnEyrDJeRI0SeN/iCXHXfFivR0vFq3vr+9naMBczAR2AEidtps5KbJrKqdZnjPFRbmfVtzWr/LlvVCub3u13Pub6TdKIVBTny1PuZ5X8GvdxMNVig89jGjvzhhWuQRaz3VhJnTra4=
|
||||
matrix:
|
||||
fast_finish: true
|
||||
# COVERALLS_TOKEN
|
||||
- secure: h/cUq+TrUMZOQmkFD7CvuwX0uAwmjIfKZ4qSUzY+QzUtDzOzA0L/XF84xTBq1Q5YYsEiaoF6GxxGCdrLQiBA/ZTd+88UHgeZPMRvi0xG9Q+PeePVOsZMTxy4/WWFgOfSQCk49Mj9zizGgO78i6vxq+SDXMtFHnZ+TpPJIEW6/m0=
|
||||
notifications:
|
||||
irc:
|
||||
use_notice: true
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -1,8 +1,3 @@
|
||||
## 0.14.8 (March 29, 2016)
|
||||
|
||||
### React
|
||||
- Fixed memory leak when rendering on the server
|
||||
|
||||
## 0.14.7 (January 28, 2016)
|
||||
|
||||
### React
|
||||
@@ -97,8 +92,8 @@
|
||||
|
||||
- `React.initializeTouchEvents` is no longer necessary and has been removed completely. Touch events now work automatically.
|
||||
- Add-Ons: Due to the DOM node refs change mentioned above, `TestUtils.findAllInRenderedTree` and related helpers are no longer able to take a DOM component, only a custom component.
|
||||
- The `props` object is now frozen, so mutating props after creating a component element is no longer supported. In most cases, [`React.cloneElement`](/react/docs/top-level-api.html#react.cloneelement) should be used instead. This change makes your components easier to reason about and enables the compiler optimizations mentioned above.
|
||||
- Plain objects are no longer supported as React children; arrays should be used instead. You can use the [`createFragment`](/react/docs/create-fragment.html) helper to migrate, which now returns an array.
|
||||
- The `props` object is now frozen, so mutating props after creating a component element is no longer supported. In most cases, [`React.cloneElement`](https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement) should be used instead. This change makes your components easier to reason about and enables the compiler optimizations mentioned above.
|
||||
- Plain objects are no longer supported as React children; arrays should be used instead. You can use the [`createFragment`](https://facebook.github.io/react/docs/create-fragment.html) helper to migrate, which now returns an array.
|
||||
- Add-Ons: `classSet` has been removed. Use [classnames](https://github.com/JedWatson/classnames) instead.
|
||||
- Web components (custom elements) now use native property names. Eg: `class` instead of `className`.
|
||||
|
||||
@@ -106,9 +101,9 @@
|
||||
|
||||
- `this.getDOMNode()` is now deprecated and `ReactDOM.findDOMNode(this)` can be used instead. Note that in the common case, `findDOMNode` is now unnecessary since a ref to the DOM component is now the actual DOM node.
|
||||
- `setProps` and `replaceProps` are now deprecated. Instead, call ReactDOM.render again at the top level with the new props.
|
||||
- ES6 component classes must now extend `React.Component` in order to enable stateless function components. The [ES3 module pattern](/react/blog/2015/01/27/react-v0.13.0-beta-1.html#other-languages) will continue to work.
|
||||
- ES6 component classes must now extend `React.Component` in order to enable stateless function components. The [ES3 module pattern](https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#other-languages) will continue to work.
|
||||
- Reusing and mutating a `style` object between renders has been deprecated. This mirrors our change to freeze the `props` object.
|
||||
- Add-Ons: `cloneWithProps` is now deprecated. Use [`React.cloneElement`](/react/docs/top-level-api.html#react.cloneelement) instead (unlike `cloneWithProps`, `cloneElement` does not merge `className` or `style` automatically; you can merge them manually if needed).
|
||||
- Add-Ons: `cloneWithProps` is now deprecated. Use [`React.cloneElement`](https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement) instead (unlike `cloneWithProps`, `cloneElement` does not merge `className` or `style` automatically; you can merge them manually if needed).
|
||||
- Add-Ons: To improve reliability, `CSSTransitionGroup` will no longer listen to transition events. Instead, you should specify transition durations manually using props such as `transitionEnterTimeout={500}`.
|
||||
|
||||
### Notable enhancements
|
||||
@@ -149,7 +144,7 @@
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- The `react-tools` package and `JSXTransformer.js` browser file [have been deprecated](/react/blog/2015/06/12/deprecating-jstransform-and-react-tools.html). You can continue using version `0.13.3` of both, but we no longer support them and recommend migrating to [Babel](http://babeljs.io/), which has built-in support for React and JSX.
|
||||
- The `react-tools` package and `JSXTransformer.js` browser file [have been deprecated](https://facebook.github.io/react/blog/2015/06/12/deprecating-jstransform-and-react-tools.html). You can continue using version `0.13.3` of both, but we no longer support them and recommend migrating to [Babel](http://babeljs.io/), which has built-in support for React and JSX.
|
||||
|
||||
#### New Features
|
||||
|
||||
@@ -251,7 +246,7 @@
|
||||
|
||||
* Support for using ES6 classes to build React components; see the [v0.13.0 beta 1 notes](https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html) for details.
|
||||
* Added new top-level API `React.findDOMNode(component)`, which should be used in place of `component.getDOMNode()`. The base class for ES6-based components will not have `getDOMNode`. This change will enable some more patterns moving forward.
|
||||
* Added a new top-level API `React.cloneElement(el, props)` for making copies of React elements – see the [v0.13 RC2 notes](/react/blog/2015/03/03/react-v0.13-rc2.html#react.cloneelement) for more details.
|
||||
* Added a new top-level API `React.cloneElement(el, props)` for making copies of React elements – see the [v0.13 RC2 notes](https://facebook.github.io/react/blog/2015/03/03/react-v0.13-rc2.html#react.cloneelement) for more details.
|
||||
* New `ref` style, allowing a callback to be used in place of a name: `<Photo ref={(c) => this._photo = c} />` allows you to reference the component with `this._photo` (as opposed to `ref="photo"` which gives `this.refs.photo`).
|
||||
* `this.setState()` can now take a function as the first argument for transactional state updates, such as `this.setState((state, props) => ({count: state.count + 1}));` – this means that you no longer need to use `this._pendingState`, which is now gone.
|
||||
* Support for iterators and immutable-js sequences as children.
|
||||
@@ -265,7 +260,7 @@
|
||||
|
||||
#### New Features
|
||||
|
||||
* [`React.addons.createFragment` was added](/react/docs/create-fragment.html) for adding keys to entire sets of children.
|
||||
* [`React.addons.createFragment` was added](https://facebook.github.io/react/docs/create-fragment.html) for adding keys to entire sets of children.
|
||||
|
||||
#### Deprecations
|
||||
|
||||
|
||||
@@ -14,6 +14,20 @@ Some of the core team will be working directly on GitHub. These changes will be
|
||||
|
||||
We will do our best to keep `master` in good shape, with tests passing at all times. But in order to move fast, we will make API changes that your application might not be compatible with. We will do our best to communicate these changes and always version appropriately so you can lock into a specific version if need be.
|
||||
|
||||
### Test Suite
|
||||
|
||||
Use `grunt test` to run the full test suite with PhantomJS.
|
||||
|
||||
This command is just a facade to [Jest](https://facebook.github.io/jest/). You may optionally run `npm install -g jest-cli` and use Jest commands directly to have more control over how tests are executed.
|
||||
|
||||
For example, `jest --watch` lets you automatically run the test suite on every file change.
|
||||
|
||||
You can also run a subset of tests by passing a prefix to `jest`. For instance, `jest ReactDOMSVG` will only run tests in the files that start with `ReactDOMSVG`, such as `ReactDOMSVG-test.js`.
|
||||
|
||||
When you know which tests you want to run, you can achieve a fast feedback loop by using these two features together. For example, `jest ReactDOMSVG --watch` will re-run only the matching tests on every change.
|
||||
|
||||
Just make sure to run the whole test suite before submitting a pull request!
|
||||
|
||||
### Pull Requests
|
||||
|
||||
The core team will be monitoring for pull requests. When we get one, we'll run some Facebook-specific integration tests on it first. From here, we'll need to get another person to sign off on the changes and then merge the pull request. For API changes we may need to fix internal uses, which could cause some delay. We'll do our best to provide updates and feedback throughout the process.
|
||||
|
||||
23
Gruntfile.js
23
Gruntfile.js
@@ -1,8 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
var assign = require('object-assign');
|
||||
var path = require('path');
|
||||
|
||||
var GULP_EXE = 'gulp';
|
||||
if (process.platform === 'win32') {
|
||||
GULP_EXE += '.cmd';
|
||||
}
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.initConfig({
|
||||
@@ -17,20 +21,19 @@ module.exports = function(grunt) {
|
||||
'./examples/shared/*.js',
|
||||
'.module-cache',
|
||||
],
|
||||
/*eslint-disable camelcase */
|
||||
compare_size: require('./grunt/config/compare_size'),
|
||||
/*eslint-enable camelcase */
|
||||
'compare_size': require('./grunt/config/compare_size'),
|
||||
});
|
||||
|
||||
grunt.config.set('compress', require('./grunt/config/compress'));
|
||||
|
||||
function spawnGulp(args, opts, done) {
|
||||
|
||||
grunt.util.spawn({
|
||||
// This could be more flexible (require.resolve & lookup bin in package)
|
||||
// but if it breaks we'll fix it then.
|
||||
cmd: path.join('node_modules', '.bin', 'gulp'),
|
||||
cmd: path.join('node_modules', '.bin', GULP_EXE),
|
||||
args: args,
|
||||
opts: assign({stdio: 'inherit'}, opts),
|
||||
opts: Object.assign({stdio: 'inherit'}, opts),
|
||||
}, function(err, result, code) {
|
||||
if (err) {
|
||||
grunt.fail.fatal('Something went wrong running gulp: ', result);
|
||||
@@ -106,10 +109,12 @@ module.exports = function(grunt) {
|
||||
]);
|
||||
grunt.registerTask('build:react-dom', require('./grunt/tasks/react-dom'));
|
||||
|
||||
grunt.registerTask('test', ['jest']);
|
||||
grunt.registerTask('npm:test', ['build', 'npm:pack']);
|
||||
var jestTasks = require('./grunt/tasks/jest');
|
||||
grunt.registerTask('jest:normal', jestTasks.normal);
|
||||
grunt.registerTask('jest:coverage', jestTasks.coverage);
|
||||
|
||||
grunt.registerTask('jest', require('./grunt/tasks/jest'));
|
||||
grunt.registerTask('test', ['jest:normal']);
|
||||
grunt.registerTask('npm:test', ['build', 'npm:pack']);
|
||||
|
||||
// Optimized build task that does all of our builds. The subtasks will be run
|
||||
// in order so we can take advantage of that and only run build-modules once.
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -2,7 +2,7 @@ BSD License
|
||||
|
||||
For React software
|
||||
|
||||
Copyright (c) 2013-2015, Facebook, Inc.
|
||||
Copyright (c) 2013-present, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
|
||||
24
README.md
24
README.md
@@ -1,4 +1,4 @@
|
||||
# [React](https://facebook.github.io/react/) [](https://travis-ci.org/facebook/react) [](http://badge.fury.io/js/react)
|
||||
# [React](https://facebook.github.io/react/) [](https://travis-ci.org/facebook/react) [](https://coveralls.io/github/facebook/react?branch=master) [](https://www.npmjs.com/package/react)
|
||||
|
||||
React is a JavaScript library for building user interfaces.
|
||||
|
||||
@@ -33,16 +33,16 @@ You'll notice that we used an HTML-like syntax; [we call it JSX](https://faceboo
|
||||
|
||||
## Installation
|
||||
|
||||
The fastest way to get started is to serve JavaScript from the CDN (also available on [cdnjs](https://cdnjs.com/libraries/react) and [jsdelivr](http://www.jsdelivr.com/#!react)):
|
||||
The fastest way to get started is to serve JavaScript from the CDN (also available on [cdnjs](https://cdnjs.com/libraries/react) and [jsdelivr](https://www.jsdelivr.com/projects/react)):
|
||||
|
||||
```html
|
||||
<!-- The core React library -->
|
||||
<script src="https://fb.me/react-0.14.8.js"></script>
|
||||
<script src="https://fb.me/react-0.14.7.js"></script>
|
||||
<!-- The ReactDOM Library -->
|
||||
<script src="https://fb.me/react-dom-0.14.8.js"></script>
|
||||
<script src="https://fb.me/react-dom-0.14.7.js"></script>
|
||||
```
|
||||
|
||||
We've also built a [starter kit](https://facebook.github.io/react/downloads/react-0.14.8.zip) which might be useful if this is your first time using React. It includes a webpage with an example of using React with live code.
|
||||
We've also built a [starter kit](https://facebook.github.io/react/downloads/react-0.14.7.zip) which might be useful if this is your first time using React. It includes a webpage with an example of using React with live code.
|
||||
|
||||
If you'd like to use [bower](http://bower.io), it's as easy as:
|
||||
|
||||
@@ -50,6 +50,12 @@ If you'd like to use [bower](http://bower.io), it's as easy as:
|
||||
bower install --save react
|
||||
```
|
||||
|
||||
And it's just as easy with [npm](http://npmjs.com):
|
||||
|
||||
```sh
|
||||
npm i --save react
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
The main purpose of this repository is to continue to evolve React core, making it faster and easier to use. If you're interested in helping with that, then keep reading. If you're not interested in helping right now that's ok too. :) Any feedback you have about using React would be greatly appreciated.
|
||||
@@ -60,7 +66,7 @@ The process to build `react.js` is built entirely on top of node.js, using many
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
* You have `node` installed at v0.10.0+ (it might work at lower versions, we just haven't tested) and `npm` at v2.0.0+.
|
||||
* You have `node` installed at v4.0.0+ and `npm` at v2.0.0+.
|
||||
* You are familiar with `npm` and know whether or not you need to use `sudo` when installing packages globally.
|
||||
* You are familiar with `git`.
|
||||
|
||||
@@ -84,14 +90,16 @@ We use grunt to automate many tasks. Run `grunt -h` to see a mostly complete lis
|
||||
```sh
|
||||
# Build and run tests with PhantomJS
|
||||
grunt test
|
||||
# Build and run tests in your browser
|
||||
grunt test --debug
|
||||
# Lint the code with ESLint
|
||||
grunt lint
|
||||
# Wipe out build directory
|
||||
grunt clean
|
||||
```
|
||||
|
||||
### Good First Bug
|
||||
To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first bugs](https://github.com/facebook/react/labels/good%20first%20bug) that contain bugs which are fairly easy to fix. This is a great place to get started.
|
||||
|
||||
|
||||
### License
|
||||
|
||||
React is [BSD licensed](./LICENSE). We also provide an additional [patent grant](./PATENTS).
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
require('rubygems')
|
||||
require('json')
|
||||
require('yaml')
|
||||
require('open-uri')
|
||||
|
||||
desc "download babel-browser"
|
||||
task :fetch_remotes do
|
||||
IO.copy_stream(
|
||||
open('https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js'),
|
||||
'js/babel-browser.min.js'
|
||||
)
|
||||
end
|
||||
|
||||
desc "generate js from jsx"
|
||||
task :js do
|
||||
system "cp ../node_modules/babel/node_modules/babel-core/browser.min.js ./js/babel-browser.min.js"
|
||||
system "../node_modules/babel/bin/babel.js _js --out-dir=js"
|
||||
system "../node_modules/.bin/babel _js --out-dir=js"
|
||||
end
|
||||
|
||||
desc "watch js"
|
||||
@@ -54,7 +62,7 @@ task :update_acknowledgements do
|
||||
end
|
||||
|
||||
desc "build into ../../react-gh-pages"
|
||||
task :release => [:update_version, :default] do
|
||||
task :release => [:update_version, :js, :fetch_remotes] do
|
||||
system "jekyll build -d ../../react-gh-pages"
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
<link rel="shortcut icon" href="/react/favicon.ico">
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ site.name }}" href="{{ site.url }}{{ site.baseurl }}/feed.xml">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css" />
|
||||
<link rel="stylesheet" href="/react/css/syntax.css">
|
||||
<link rel="stylesheet" href="/react/css/codemirror.css">
|
||||
<link rel="stylesheet" href="/react/css/react.css">
|
||||
@@ -29,7 +28,6 @@
|
||||
<script src="/react/js/es5-shim.min.js"></script>
|
||||
<script src="/react/js/es5-sham.min.js"></script>
|
||||
<![endif]-->
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script>
|
||||
<script src="/react/js/codemirror.js"></script>
|
||||
<script src="/react/js/javascript.js"></script>
|
||||
<script src="/react/js/react.js"></script>
|
||||
@@ -52,9 +50,6 @@
|
||||
<li><a href="/react/support.html"{% if page.id == 'support' %} class="active"{% endif %}>Support</a></li>
|
||||
<li><a href="/react/downloads.html"{% if page.id == 'downloads' %} class="active"{% endif %}>Download</a></li>
|
||||
<li><a href="/react/blog/"{% if page.sectionid == 'blog' %} class="active"{% endif %}>Blog</a></li>
|
||||
<li>
|
||||
<input id="algolia-doc-search" type="text" placeholder="Search docs..." />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav-site nav-site-external">
|
||||
@@ -112,12 +107,6 @@
|
||||
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=623268441017527";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
|
||||
docsearch({
|
||||
apiKey: '36221914cce388c46d0420343e0bb32e',
|
||||
indexName: 'react',
|
||||
inputSelector: '#algolia-doc-search'
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -56,7 +56,7 @@ The video will be available soon on the [JSConf EU website](http://2013.jsconf.e
|
||||
|
||||
[Todd Kennedy](http://blog.selfassembled.org/) working at [Condé Nast](http://www.condenast.com/) implemented a wrapper on-top of [JSHint](http://www.jshint.com/) that first converts JSX files to JS.
|
||||
|
||||
> A wrapper around JSHint to allow linting of files containg JSX syntax. Accepts glob patterns. Respects your local .jshintrc file and .gitignore to filter your glob patterns.
|
||||
> A wrapper around JSHint to allow linting of files containing JSX syntax. Accepts glob patterns. Respects your local .jshintrc file and .gitignore to filter your glob patterns.
|
||||
>
|
||||
> ```
|
||||
npm install -g jsxhint
|
||||
|
||||
@@ -13,7 +13,7 @@ React is only one-piece of your web application stack. [Mark Lussier](https://gi
|
||||
>
|
||||
> I encourage you to fork, and make it right and submit a pull request!
|
||||
>
|
||||
> My current opinion is using tools like Grunt, Browserify, Bower and mutiple grunt plugins to get the job done. I also opted for Zepto over jQuery and the Flatiron Project's Director when I need a router. Oh and for the last little bit of tech that makes you mad, I am in the SASS camp when it comes to stylesheets
|
||||
> My current opinion is using tools like Grunt, Browserify, Bower and multiple grunt plugins to get the job done. I also opted for Zepto over jQuery and the Flatiron Project's Director when I need a router. Oh and for the last little bit of tech that makes you mad, I am in the SASS camp when it comes to stylesheets
|
||||
>
|
||||
> [Check it out on GitHub...](https://github.com/intabulas/reactjs-baseline)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ React.DOM.div(null,
|
||||
)
|
||||
```
|
||||
|
||||
We believe this new behavior is more helpful and elimates cases where unwanted whitespace was previously added.
|
||||
We believe this new behavior is more helpful and eliminates cases where unwanted whitespace was previously added.
|
||||
|
||||
In cases where you want to preserve the space adjacent to a newline, you can write a JS string like `{"Monkeys: "}` in your JSX source. We've included a script to do an automated codemod of your JSX source tree that preserves the old whitespace behavior by adding and removing spaces appropriately. You can [install jsx\_whitespace\_transformer from npm](https://github.com/facebook/react/blob/master/npm-jsx_whitespace_transformer/README.md) and run it over your source tree to modify files in place. The transformed JSX files will preserve your code's existing whitespace behavior.
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ React.DOM.div(null,
|
||||
)
|
||||
```
|
||||
|
||||
We believe this new behavior is more helpful and elimates cases where unwanted whitespace was previously added.
|
||||
We believe this new behavior is more helpful and eliminates cases where unwanted whitespace was previously added.
|
||||
|
||||
In cases where you want to preserve the space adjacent to a newline, you can write `{'Monkeys: '}` or `Monkeys:{' '}` in your JSX source. We've included a script to do an automated codemod of your JSX source tree that preserves the old whitespace behavior by adding and removing spaces appropriately. You can [install jsx\_whitespace\_transformer from npm](https://github.com/facebook/react/blob/master/npm-jsx_whitespace_transformer/README.md) and run it over your source tree to modify files in place. The transformed JSX files will preserve your code's existing whitespace behavior.
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
title: "React v15.0 Release Candidate"
|
||||
author: zpao
|
||||
---
|
||||
|
||||
Sorry for the small delay in releasing this. As we said, we've been busy binge-watching House of Cards. That scene in the last episode where Francis and Claire Underwood <abbr title="You didn't think we would actually spoil anything did you?">████████████████████████████████████</abbr>. WOW!
|
||||
|
||||
But now we're ready, so without further ado, we're shipping a release candidate for React v15 now. As a reminder, [we're switching to major versions](/react/blog/2016/02/19/new-versioning-scheme.html) to indicate that we have been using React in production for a long time. This 15.0 release follows our previous 0.14 version and we'll continue to follow semver like we've been doing since 2013. It's also worth noting that [we no longer actively support Internet Explorer 8](/react/blog/2016/01/12/discontinuing-ie8-support.html). We believe React will work in its current form there but we will not be prioritizing any efforts to fix new issues that only affect IE8.
|
||||
|
||||
Please try it out before we publish the final release. Let us know if you run into any problems by filing issues on our [GitHub repo](https://github.com/facebook/react).
|
||||
|
||||
## Upgrade Guide
|
||||
|
||||
Like always, we have a few breaking changes in this release. We know changes can be painful (the Facebook codebase has over 15,000 React components), so we always try to make changes gradually in order to minimize the pain.
|
||||
|
||||
If your code is free of warnings when running under React 0.14, upgrading should be easy. The bulk of changes in this release are actually behind the scenes, impacting the way that React interacts with the DOM. The other substantial change is that React now supports the full range of SVG elements and attributes. Beyond that we have a large number of incremental improvements and additional warnings aimed to aid developers. We've also laid some groundwork in the core to bring you some new capabilities in future releases.
|
||||
|
||||
See the changelog below for more details.
|
||||
|
||||
## Installation
|
||||
|
||||
We recommend using React from `npm` and using a tool like browserify or webpack to build your code into a single bundle. To install the two packages:
|
||||
|
||||
* `npm install --save react@15.0.0-rc.1 react-dom@15.0.0-rc.1`
|
||||
|
||||
Remember that by default, React runs extra checks and provides helpful warnings in development mode. When deploying your app, set the `NODE_ENV` environment variable to `production` to use the production build of React which does not include the development warnings and runs significantly faster.
|
||||
|
||||
If you can’t use `npm` yet, we provide pre-built browser builds for your convenience, which are also available in the `react` package on bower.
|
||||
|
||||
* **React**
|
||||
Dev build with warnings: <https://fb.me/react-15.0.0-rc.1.js>
|
||||
Minified build for production: <https://fb.me/react-15.0.0-rc.1.min.js>
|
||||
* **React with Add-Ons**
|
||||
Dev build with warnings: <https://fb.me/react-with-addons-15.0.0-rc.1.js>
|
||||
Minified build for production: <https://fb.me/react-with-addons-15.0.0-rc.1.min.js>
|
||||
* **React DOM** (include React in the page before React DOM)
|
||||
Dev build with warnings: <https://fb.me/react-dom-15.0.0-rc.1.js>
|
||||
Minified build for production: <https://fb.me/react-dom-15.0.0-rc.1.min.js>
|
||||
|
||||
## Changelog
|
||||
|
||||
### Major changes
|
||||
|
||||
- #### `document.createElement` is in and `data-reactid` is out
|
||||
|
||||
There were a number of large changes to our interactions with the DOM. One of the most noticeable changes is that we no longer set the `data-reactid` attribute for each DOM node. While this will make it much more difficult to know if a website is using React, the advantage is that the DOM is much more lightweight. This change was made possible by us switching to use `document.createElement` on initial render. Previously we would generate a large string of HTML and then set `node.innerHTML`. At the time, this was decided to be faster than using `document.createElement` for the majority of cases and browsers that we supported. Browsers have continued to improve and so overwhelmingly this is no longer true. By using `createElement` we can make other parts of React faster. The ids were used to map back from events to the original React component, meaning we had to do a bunch of work on every event, even though we cached this data heavily. As we've all experienced, caching and in particularly invalidating caches, can be error prone and we saw many hard to reproduce issues over the years as a result. Now we can build up a direct mapping at render time since we already have a handle on the node.
|
||||
|
||||
- #### No more extra `<span>`s
|
||||
|
||||
Another big change with our DOM interaction is how we render text blocks. Previously you may have noticed that React rendered a lot of extra `<span>`s. Eg, in our most basic example on the home page we render `<div>Hello {this.props.name}</div>`, resulting in markup that contained 2 `<span>`s. Now we'll render plain text nodes interspersed with comment nodes that are used for demarcation. This gives us the same ability to update individual pieces of text, without creating extra nested nodes. Very few people have depended on the actual markup generated here so it's likely you are not impacted. However if you were targeting these `<span>`s in your CSS, you will need to adjust accordingly. You can always render them explicitly in your components.
|
||||
|
||||
- #### Rendering `null` now uses comment nodes
|
||||
|
||||
We've also made use of these comment nodes to change what `null` renders to. Rendering to `null` was a feature we added in React v0.11 and was implemented by rendering `<noscript>` elements. By rendering to comment nodes now, there's a chance some of your CSS will be targeting the wrong thing, specifically if you are making use of `:nth-child` selectors. This, along with the other changes mentioned above, have always been considered implementation details of how React targets the DOM. We believe they are safe changes to make without going through a release with warnings detailing the subtle differences as they are details that should not be depended upon. Additionally, we have seen that these changes have improved React performance for many typical applications.
|
||||
|
||||
- #### Improved SVG support
|
||||
|
||||
All SVG tags and attributes are now fully supported. (Uncommon SVG tags are not present on the `React.DOM` element helper, but JSX and `React.createElement` work on all tag names.) All SVG attributes match their original capitalization and hyphenation as defined in the specification (ex: `gradientTransform` must be camel-cased but `clip-path` should be hyphenated).
|
||||
|
||||
|
||||
|
||||
### Breaking changes
|
||||
|
||||
It's worth calling out the DOM structure changes above again, in particular the change from `<span>`s. In the course of updating the Facebook codebase, we found a very small amount of code that was depending on the markup that React generated. Some of these cases were integration tests like WebDriver which were doing very specific XPath queries to target nodes. Others were simply tests using `ReactDOM.renderToStaticMarkup` and comparing markup. Again, there were a very small number of changes that had to be made, but we don't want anybody to be blindsided. We encourage everybody to run their test suites when upgrading and consider alternative approaches when possible. One approach that will work for some cases is to explicitly use `<span>`s in your `render` method.
|
||||
|
||||
These deprecations were introduced in v0.14 with a warning and the APIs are now removed.
|
||||
|
||||
- Deprecated APIs removed from `React`, specifically `findDOMNode`, `render`, `renderToString`, `renderToStaticMarkup`, and `unmountComponentAtNode`.
|
||||
- Deprecated APIs removed from `React.addons`, specifically `batchedUpdates` and `cloneWithProps`.
|
||||
- Deprecated APIs removed from component instances, specifically `setProps`, `replaceProps`, and `getDOMNode`.
|
||||
|
||||
### New deprecations, introduced with a warning
|
||||
|
||||
Each of these changes will continue to work as before with a new warning until the release of React 16 so you can upgrade your code gradually.
|
||||
|
||||
- `LinkedStateMixin` and `valueLink` are now deprecated due to very low popularity. If you need this, you can use a wrapper component that implements the same behavior: [react-linked-input](https://www.npmjs.com/package/react-linked-input).
|
||||
|
||||
|
||||
### New helpful warnings
|
||||
|
||||
- If you use a minified copy of the _development_ build, React DOM kindly encourages you to use the faster production build instead.
|
||||
- React DOM: When specifying a unit-less CSS value as a string, a future version will not add `px` automatically. This version now warns in this case (ex: writing `style={{'{{'}}width: '300'}}`. (Unitless *number* values like `width: 300` are unchanged.)
|
||||
- Synthetic Events will now warn when setting and accessing properties (which will not get cleared appropriately), as well as warn on access after an event has been returned to the pool.
|
||||
- Elements will now warn when attempting to read `ref` and `key` from the props.
|
||||
- React DOM now attempts to warn for mistyped event handlers on DOM elements (ex: `onclick` which should be `onClick`)
|
||||
|
||||
### Notable bug fixes
|
||||
|
||||
- Fixed multiple small memory leaks
|
||||
- Click events are handled by React DOM more reliably in mobile browsers, particularly in Mobile Safari.
|
||||
- Input events are handled more reliably in IE 10 and IE 11; spurious events no longer fire when using a placeholder.
|
||||
- React DOM now supports the `cite` and `profile` HTML attributes.
|
||||
- React DOM now supports the `onAnimationStart`, `onAnimationEnd`, `onAnimationIteration`, `onTransitionEnd`, and `onInvalid` events. Support for `onLoad` has been added to `object` elements.
|
||||
- Add-Ons: ReactTransitionGroup now correctly handles multiple nodes being removed simultaneously.
|
||||
- `Object.is` is used in a number of places to compare values, which leads to fewer false positives, especially involving `NaN`. In particular, this affects the `shallowCompare` add-on.
|
||||
- React DOM now defaults to using DOM attributes instead of properties, which fixes a few edge case bugs. Additionally the nullification of values (ex: `href={null}`) now results in the forceful removal, no longer trying to set to the default value used by browsers in the absence of a value.
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
title: "React v15.0 Release Candidate 2"
|
||||
author: zpao
|
||||
---
|
||||
|
||||
Today we're releasing a second release candidate for version 15. Primarily this is to address 2 issues, but we also picked up a few small changes from new contributors, including some improvements to some of our new warnings.
|
||||
|
||||
The most pressing change that was made is to fix a bug in our new code that removes `<span>`s, as discussed in the original RC1 post. Specifically we have some code that takes a different path in IE11 and Edge due to the speed of some DOM operations. There was a bug in this code which didn't break out of the optimization for `DocumentFragment`s, resulting in text not appearing at all. Thanks to the several people who [reported this](https://github.com/facebook/react/issues/6246).
|
||||
|
||||
The other change is to our SVG code. In RC1 we had made the decision to pass through all attributes directly. This led to [some confusion with `class` vs `className`](https://github.com/facebook/react/issues/6211) and ultimately led us to reconsider our position on the approach. Passing through all attributes meant that we would have two different patterns for using React where things like hyphenated attributes would work for SVG but not HTML. In the future, we *might* change our approach to the problem for HTML as well but in the meantime, maintaining consistency is important. So we reverted the changes that allowed the attributes to be passed through and instead expanded the SVG property list to include all attributes that are in the spec. We believe we have everything now but definitely [let us know](https://github.com/facebook/react/issues/1657#issuecomment-197031403) if we missed anything. It was and still is our intent to support the full range of SVG tags and attributes in this release.
|
||||
|
||||
Thanks again to everybody who has tried the RC1 and reported issues. It has been extremely important and we wouldn't be able to do this without your help!
|
||||
|
||||
## Installation
|
||||
|
||||
We recommend using React from `npm` and using a tool like browserify or webpack to build your code into a single bundle. To install the two packages:
|
||||
|
||||
* `npm install --save react@15.0.0-rc.2 react-dom@15.0.0-rc.2`
|
||||
|
||||
Remember that by default, React runs extra checks and provides helpful warnings in development mode. When deploying your app, set the `NODE_ENV` environment variable to `production` to use the production build of React which does not include the development warnings and runs significantly faster.
|
||||
|
||||
If you can’t use `npm` yet, we provide pre-built browser builds for your convenience, which are also available in the `react` package on bower.
|
||||
|
||||
* **React**
|
||||
Dev build with warnings: <https://fb.me/react-15.0.0-rc.2.js>
|
||||
Minified build for production: <https://fb.me/react-15.0.0-rc.2.min.js>
|
||||
* **React with Add-Ons**
|
||||
Dev build with warnings: <https://fb.me/react-with-addons-15.0.0-rc.2.js>
|
||||
Minified build for production: <https://fb.me/react-with-addons-15.0.0-rc.2.min.js>
|
||||
* **React DOM** (include React in the page before React DOM)
|
||||
Dev build with warnings: <https://fb.me/react-dom-15.0.0-rc.2.js>
|
||||
Minified build for production: <https://fb.me/react-dom-15.0.0-rc.2.min.js>
|
||||
@@ -5,7 +5,7 @@
|
||||
font-family: monospace;
|
||||
}
|
||||
.CodeMirror-scroll {
|
||||
/* Set scrolling behaviour here */
|
||||
/* Set scrolling behavior here */
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
||||
}
|
||||
|
||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||
before actuall scrolling happens, thus preventing shaking and
|
||||
before actual scrolling happens, thus preventing shaking and
|
||||
flickering artifacts. */
|
||||
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
|
||||
position: absolute;
|
||||
|
||||
@@ -129,7 +129,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li > a {
|
||||
a {
|
||||
box-sizing: content-box;
|
||||
padding: 0 10px;
|
||||
line-height: $navHeight;
|
||||
@@ -759,6 +759,8 @@ p a code {
|
||||
margin-bottom: 0;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
float: left;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.highlight pre code {
|
||||
@@ -854,72 +856,3 @@ div[data-twttr-id] iframe {
|
||||
.three-column > ul:first-child {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
|
||||
/* Algolia Doc Search */
|
||||
|
||||
@media screen and (max-width: 960px) {
|
||||
#algolia-doc-search {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
input#algolia-doc-search {
|
||||
background: transparent url('/react/img/search.png') no-repeat 10px center;
|
||||
background-size: 16px 16px;
|
||||
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
margin-left: 10px;
|
||||
padding: 0 10px;
|
||||
padding-left: 35px;
|
||||
height: 30px;
|
||||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
background-color: #333;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
outline: none;
|
||||
width: 170px;
|
||||
|
||||
transition: width .2s ease;
|
||||
}
|
||||
|
||||
input#algolia-doc-search:focus {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.algolia-autocomplete .aa-dropdown-menu {
|
||||
margin-left: -110px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.algolia-autocomplete {
|
||||
vertical-align: top;
|
||||
height: 53px;
|
||||
}
|
||||
|
||||
.algolia-docsearch-suggestion {
|
||||
border-bottom-color: #c05b4d;
|
||||
}
|
||||
|
||||
.algolia-docsearch-suggestion--category-header {
|
||||
background-color: #cc7a6f;
|
||||
}
|
||||
|
||||
.algolia-docsearch-suggestion--highlight {
|
||||
color: #c05b4d;
|
||||
}
|
||||
|
||||
.algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight {
|
||||
background-color: #c05b4d;
|
||||
}
|
||||
|
||||
.aa-cursor .algolia-docsearch-suggestion--content {
|
||||
color: #c05b4d;
|
||||
}
|
||||
|
||||
.aa-cursor .algolia-docsearch-suggestion {
|
||||
background: #f1f3f5;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ title: 为什么使用 React?
|
||||
permalink: why-react-zh-CN.html
|
||||
next: displaying-data-zh-CN.html
|
||||
---
|
||||
|
||||
React 是一个 Facebook 和 Instagram 用来创建用户界面的 JavaScript 库。很多人选择将 React 认为是 **[MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)** 中的 **V**(视图)。
|
||||
|
||||
我们创造 React 是为了解决一个问题:**构建随着时间数据不断变化的大规模应用程序**。
|
||||
@@ -27,3 +28,5 @@ React挑战了很多传统的知识,第一眼看上去可能很多想法有点
|
||||
## 了解更多
|
||||
|
||||
你可以从这篇[博客](/react/blog/2013/06/05/why-react.html)了解更多我们创造 React 的动机。
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ setInterval(function() {
|
||||
|
||||
このことについて私たちが理解できる方法は、Reactは必要になるまで、DOMの操作を行わないということです。 **Reactは、DOMの変化を表現し、あなたにもっとも効率的なDOMの変化を見積もるために早い、内部のモックのDOMを使っています。**
|
||||
|
||||
このコンポーネントのインプットは `props` と呼ばれるものです。"properties" の省略形です。それらはJSXシンタックスの中でアトリビュートとして渡されます。それらはコンポーネントの中で不変と考えるべきで、 **`this.props` と書かないようにしてください**
|
||||
このコンポーネントのインプットは `props` と呼ばれるものです。"properties" の省略形です。それらはJSXシンタックスの中でアトリビュートとして渡されます。それらはコンポーネントの中で不変と考えるべきで、 **`this.props` には書き込まないようにしてください**
|
||||
|
||||
## コンポーネントは関数のようなものです。
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ JSX 类似于 HTML,但不是完全一样。参考 [JSX 陷阱](/react/docs/jsx
|
||||
|
||||
## 没有 JSX 的 React
|
||||
|
||||
JSX完全是可选的;你无需在 React 中必须使用 JSX。你可以通过 `React.createElement` 来创建一个树。第一个参数是标签,第二个参数是一个属性对象,第三个是子节点。
|
||||
JSX完全是可选的;你无需在 React 中必须使用 JSX。你可以通过 `React.createElement` 来创建一个树。第一个参数是标签,第二个参数是一个属性对象,每三个是子节点。
|
||||
|
||||
```javascript
|
||||
var child1 = React.createElement('li', null, 'First Text Content');
|
||||
|
||||
@@ -14,49 +14,49 @@ next: reusable-components-zh-CN.html
|
||||
|
||||
## 组合实例
|
||||
|
||||
让我们用 Facebook Graph API 来开发一个显示 Facebook 页面图片和用户名的简单 Avatar 组件吧。
|
||||
一起来使用 Facebook Graph API 开发显示个人图片和用户名的简单 Avatar 组件吧。
|
||||
|
||||
```javascript
|
||||
var Avatar = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<PagePic pagename={this.props.pagename} />
|
||||
<PageLink pagename={this.props.pagename} />
|
||||
<ProfilePic username={this.props.username} />
|
||||
<ProfileLink username={this.props.username} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var PagePic = React.createClass({
|
||||
var ProfilePic = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<img src={'https://graph.facebook.com/' + this.props.pagename + '/picture'} />
|
||||
<img src={'https://graph.facebook.com/' + this.props.username + '/picture'} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var PageLink = React.createClass({
|
||||
var ProfileLink = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<a href={'https://www.facebook.com/' + this.props.pagename}>
|
||||
{this.props.pagename}
|
||||
<a href={'https://www.facebook.com/' + this.props.username}>
|
||||
{this.props.username}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<Avatar pagename="Engineering" />,
|
||||
<Avatar username="pwh" />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
## 从属关系
|
||||
|
||||
上面例子中,`Avatar` 拥有 `PagePic` 和 `PageLink` 的实例。`拥有者` 就是给其它组件设置 `props` 的那个组件。更正式地说,如果组件 `Y` 在 `render()` 方法是创建了组件 `X`,那么 `Y` 就拥有 `X`。上面讲过,组件不能修改自身的 `props` - 它们总是与它们拥有者设置的保持一致。这是保持用户界面一致性的基本不变量。
|
||||
上面例子中,`Avatar` 拥有 `ProfilePic` 和 `ProfileLink` 的实例。`拥有者` 就是给其它组件设置 `props` 的那个组件。更正式地说,如果组件 `Y` 在 `render()` 方法是创建了组件 `X`,那么 `Y` 就拥有 `X`。上面讲过,组件不能修改自身的 `props` - 它们总是与它们拥有者设置的保持一致。这是保持用户界面一致性的基本不变量。
|
||||
|
||||
把从属关系与父子关系加以区别至关重要。从属关系是 React 特有的,而父子关系简单来讲就是DOM 里的标签的关系。在上一个例子中,`Avatar` 拥有 `div`、`PagePic` 和 `PageLink` 实例,`div` 是 `PagePic` 和 `PageLink` 实例的**父级**(但不是拥有者)。
|
||||
把从属关系与父子关系加以区别至关重要。从属关系是 React 特有的,而父子关系简单来讲就是DOM 里的标签的关系。在上一个例子中,`Avatar` 拥有 `div`、`ProfilePic` 和 `ProfileLink` 实例,`div` 是 `ProfilePic` 和 `ProfileLink` 实例的**父级**(但不是拥有者)。
|
||||
|
||||
## 子级
|
||||
|
||||
|
||||
@@ -254,4 +254,4 @@ ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);
|
||||
>
|
||||
> 因为无状态函数没有备份实例,你不能附加一个引用到一个无状态函数组件。 通常这不是问题,因为无状态函数不提供一个命令式的API。没有命令式的API,你就没有任何需要实例来做的事。然而,如果用户想查找无状态函数组件的DOM节点,他们必须把这个组件包装在一个有状态组件里(比如,ES6 类组件) 并且连接一个引用到有状态的包装组件。
|
||||
|
||||
在理想世界里,你的大多数组件都应该是无状态函数,因为将来我们可能会用避免不必要的检查和内存分配的方式来对这些组件进行优化。 如果可能,这是推荐的模式。
|
||||
在理想世界里,你的大多数组件都应该是无状态函数式的,因为这些无状态组件可以在React核心里经过一个快速的代码路径。 如果可能,这是推荐的模式。
|
||||
|
||||
@@ -96,7 +96,7 @@ function FancyCheckbox(props) {
|
||||
|
||||
## 使用和传递同一个 Prop
|
||||
|
||||
如果组件需要使用一个属性又要往下传递,可以直接使用 `checked={checked}` 再传一次。这样做比传整个 `props` 对象要好,因为更利于重构和语法检查。
|
||||
如果组件需要使用一个属性又要往下传递,可以直接使用 `checked={checked}` 再传一次。这样做比传整个 `this.props` 对象要好,因为更利于重构和语法检查。
|
||||
|
||||
```javascript
|
||||
function FancyCheckbox(props) {
|
||||
|
||||
@@ -30,11 +30,11 @@ next: working-with-the-browser-zh-CN.html
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 对于 `<input>` and `<textarea>`, `onChange` 取代 — 一般应该用来替代 — DOM内建的 [`oninput`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/oninput) 事件处理。
|
||||
> 对于 `<input>` and `<textarea>`, `onChange` 替代 — 一般应该用来替代 — the DOM's 内建的 [`oninput`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/oninput) 事件处理。
|
||||
|
||||
## 受控组件
|
||||
## 受限组件
|
||||
|
||||
一个*受控*的 `<input>` 有一个 `value` prop。渲染一个受控 `<input>` 会反映 `value` prop 的值。
|
||||
设置了 `value` 的 `<input>` 是一个*受限*组件。 对于受限的 `<input>`,渲染出来的 HTML 元素始终保持 `value` 属性的值。例如:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
@@ -42,7 +42,7 @@ next: working-with-the-browser-zh-CN.html
|
||||
}
|
||||
```
|
||||
|
||||
用户输入在被渲染的元素里将没有作用,因为 React 已经声明值为 `Hello!`。要更新 value 来响应用户输入,你可以使用 `onChange` 事件:
|
||||
上面的代码将渲染出一个值为 `Hello!` 的 input 元素。用户在渲染出来的元素里输入任何值都不起作用,因为 React 已经赋值为 `Hello!`。如果想响应更新用户输入的值,就得使用 `onChange` 事件:
|
||||
|
||||
```javascript
|
||||
getInitialState: function() {
|
||||
@@ -52,17 +52,12 @@ next: working-with-the-browser-zh-CN.html
|
||||
this.setState({value: event.target.value});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
);
|
||||
var value = this.state.value;
|
||||
return <input type="text" value={value} onChange={this.handleChange} />;
|
||||
}
|
||||
```
|
||||
|
||||
在这个例子中,我们接受用户提供的值并更新 `<input>` 组件的 `value` prop。这个模式使实现响应或者验证用户输入的界面更容易。例如:
|
||||
上面的代码中,React 将用户输入的值更新到 `<input>` 组件的 `value` 属性。这样实现响应或者验证用户输入的界面就很容易了。例如:
|
||||
|
||||
```javascript
|
||||
handleChange: function(event) {
|
||||
@@ -72,15 +67,13 @@ next: working-with-the-browser-zh-CN.html
|
||||
|
||||
上面的代码接受用户输入,并截取前 140 个字符。
|
||||
|
||||
受控组件不维持一个自己的内部状态;它单纯的基于 props 渲染。
|
||||
|
||||
### 复选框与单选按钮的潜在问题
|
||||
|
||||
当心,在力图标准化复选框与单选按钮的变换处理中,React使用`click` 事件代替 `change` 事件。在大多数情况下它们表现的如同预期,除了在`change` handler中调用`preventDefault` 。`preventDefault` 阻止了浏览器视觉上更新输入,即使`checked`被触发。变通的方式是要么移除`preventDefault`的调用,要么把`checked` 的触发放在一个`setTimeout`里。
|
||||
|
||||
## 不受控组件
|
||||
## 不受限组件
|
||||
|
||||
一个没有 `value` 属性的 `<input>` 是一个不*受控*组件:
|
||||
没有设置 `value`(或者设为 `null`) 的 `<input>` 组件是一个*不受限*组件。对于不受限的 `<input>` 组件,渲染出来的元素直接反应用户输入。例如:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
@@ -88,9 +81,7 @@ next: working-with-the-browser-zh-CN.html
|
||||
}
|
||||
```
|
||||
|
||||
上面的代码将渲染出一个空值的输入框,用户输入将立即反应到元素上。和受控元素一样,使用 `onChange` 事件可以监听值的变化。
|
||||
|
||||
*不受控*组件维持它自己的内部状态。
|
||||
上面的代码将渲染出一个空值的输入框,用户输入将立即反应到元素上。和受限元素一样,使用 `onChange` 事件可以监听值的变化。
|
||||
|
||||
### 默认值
|
||||
|
||||
@@ -102,17 +93,17 @@ next: working-with-the-browser-zh-CN.html
|
||||
}
|
||||
```
|
||||
|
||||
这个例子会像上面的 **不受控组件** 例子一样运行。
|
||||
这个例子会像上面的 **不受限组件** 例子一样运行。
|
||||
|
||||
同样的, `<input type="checkbox">` 和 `<input type="radio">` 支持 `defaultChecked` 、 `<select>` 支持 `defaultValue`.
|
||||
同样的, `<input>` 支持 `defaultChecked` 、 `<select>` 支持 `defaultValue`.
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> `defaultValue` 和 `defaultChecked` props 只能在内部渲染时被使用。 如果你需要在随后的渲染更新值, 你需要使用 [受控组件](#受控组件).
|
||||
> `defaultValue` 和 `defaultChecked` props 只能在内部渲染时被使用。 如果你需要在随后的渲染更新值, 你需要使用 [受限组件](#受限组件).
|
||||
|
||||
## 高级主题
|
||||
|
||||
### 为什么使用受控组件?
|
||||
### 为什么使用受限组件?
|
||||
|
||||
在 React 中使用诸如 `<input>` 的表单组件时,遇到了一个在传统 HTML 中没有的挑战。比如下面的代码:
|
||||
|
||||
@@ -120,9 +111,9 @@ next: working-with-the-browser-zh-CN.html
|
||||
<input type="text" name="title" value="Untitled" />
|
||||
```
|
||||
|
||||
它渲染一个*初始值*为 `Untitled` 的输入框。当用户改变输入框的值时,节点的 `value` 属性( *property*)将随之变化,但是 `node.getAttribute('value')` 还是会返回初始设置的值 `Untitled`.
|
||||
在 HTML 中将渲染 *初始值* 为 `Untitled` 的输入框。用户改变输入框的值时,节点的 `value` 属性( *property*)将随之变化,但是 `node.getAttribute('value')` 还是会返回初始设置的值 `Untitled`.
|
||||
|
||||
与 HTML 不同,React 组件必须在任何时间点表现视图的状态,而不仅仅是在初始化时。比如在 React 中:
|
||||
与 HTML 不同,React 组件必须在任何时间点描绘视图的状态,而不仅仅是在初始化时。比如在 React 中:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
@@ -130,7 +121,7 @@ next: working-with-the-browser-zh-CN.html
|
||||
}
|
||||
```
|
||||
|
||||
既然这个方法描述了在任意时间点上的视图,那么文本输入框的值就应该*始终*为 `Untitled`。
|
||||
该方法在任何时间点渲染组件以后,输入框的值就应该 *始终* 为 `Untitled`。
|
||||
|
||||
### 为什么 `<textarea>` 使用 `value` 属性?
|
||||
|
||||
@@ -141,7 +132,7 @@ next: working-with-the-browser-zh-CN.html
|
||||
<textarea name="description">This is the description.</textarea>
|
||||
```
|
||||
|
||||
对 HTML 而言,让开发者设置多行的值很容易。但是,React 是 JavaScript,没有字符串限制,可以使用 `\n` 实现换行。简言之,React 已经有 `value`、`defaultValue` 属性,`</textarea>` 组件的子节点扮演什么角色就有点模棱两可了。基于此, 设置 `<textarea>` 值时不应该使用子节点:
|
||||
对 HTML 而言,让开发者设置多行的值很容易。但是,React 是 JavaScript,没有字符限制,可以使用 `\n` 实现换行。简言之,React 已经有 `value`、`defaultValue` 属性,`</textarea>` 组件的子节点扮演什么角色就有点模棱两可了。基于此, 设置 `<textarea>` 值时不应该使用子节点:
|
||||
|
||||
```javascript
|
||||
<textarea name="description" value="This is a description." />
|
||||
@@ -161,7 +152,7 @@ HTML 中 `<select>` 通常使用 `<option>` 的 `selected` 属性设置选中状
|
||||
</select>
|
||||
```
|
||||
|
||||
如果是不受控组件,则使用 `defaultValue`。
|
||||
如果是不受限组件,则使用 `defaultValue`。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
|
||||
@@ -22,7 +22,7 @@ next: addons-zh-CN.html
|
||||
|
||||
### 浏览器中的JSX转化
|
||||
|
||||
如果你喜欢使用JSX,Babel5 提供了被称为browser.js 用于开发的一个浏览器内的 ES6 和 JSX 转换器,它可以从 [CDNJS](http://cdnjs.com/libraries/babel-core/5.8.34) 引用。Include `<script type="text/babel">` 标记来使用 JSX 转换器.
|
||||
如果你喜欢使用JSX,Babel提供了一个被称为browser.js的[开发用的浏览器中的 ES6 和 JSX 转换器](http://babeljs.io/docs/usage/browser/) ,它可以从`babel-core` npm release 或者[CDNJS](http://cdnjs.com/libraries/babel-core) 中 include。Include `<script type="text/babel">` 标记来使用 JSX 转换器.
|
||||
|
||||
> 注意:
|
||||
>
|
||||
|
||||
@@ -18,7 +18,7 @@ React插件是一系列的用来构建 React app的有用模块。 **这些应
|
||||
|
||||
下面的插件只存在开发版(未压缩)React中:
|
||||
|
||||
- [`TestUtils`](test-utils.html), 用于写测试用例的简单的辅助工具。
|
||||
- [`Perf`](perf.html), 一个用于查找优化机会的性能分析工具。
|
||||
- [`TestUtils`](test-utils.html), 用于写测试用例的简单的辅助工具(仅存在于未压缩版本)。
|
||||
- [`Perf`](perf.html), 用于测量性能并给你提示哪里可以优化。
|
||||
|
||||
要获取插件,单独从npm安装他们(例如 `npm install react-addons-pure-render-mixin`).我们不支持使用插件如果你没有用npm.
|
||||
|
||||
@@ -10,7 +10,7 @@ next: test-utils.html
|
||||
|
||||
> Note:
|
||||
>
|
||||
> If you're new to the framework, note that `ReactLink` is not needed for most applications and should be used cautiously.
|
||||
> ReactLink is deprecated as of React v15. The recommendation is to explicitly set the value and change handler, instead of using ReactLink.
|
||||
|
||||
In React, data flows one way: from owner to child. This is because data only flows one direction in [the Von Neumann model of computing](https://en.wikipedia.org/wiki/Von_Neumann_architecture). You can think of it as "one-way data binding."
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ next: test-utils-zh-CN.html
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 在 React v15 中 ReactLink 被弃用了。推荐明确的设置值和变动的处理,而不是使用 ReactLink。
|
||||
> 如果你刚学这个框架,注意 `ReactLink` 对大多数应用是不需要的,应该慎重的使用。
|
||||
|
||||
在React里,数据单向流动: 从拥有者到子级。这是因为数据只单向流动[the Von Neumann model of computing](https://en.wikipedia.org/wiki/Von_Neumann_architecture)。你可以把它想象为 “单向数据绑定”。
|
||||
|
||||
@@ -70,7 +70,7 @@ var WithLink = React.createClass({
|
||||
|
||||
## 引擎盖下
|
||||
|
||||
There are two sides to `ReactLink`: the place where you create the `ReactLink` instance and the place where you use it. To prove how simple `ReactLink` is, let's rewrite each side separately to be more explicit.
|
||||
这里对 `ReactLink`有两方面:创建`ReactLink`的实例以及使用它的地方。为了证明`ReactLink`有多简单,让我们重写两方面一边更好的理解。
|
||||
|
||||
### ReactLink Without LinkedStateMixin
|
||||
|
||||
@@ -92,7 +92,7 @@ var WithoutMixin = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
As you can see, `ReactLink` objects are very simple objects that just have a `value` and `requestChange` prop. And `LinkedStateMixin` is similarly simple: it just populates those fields with a value from `this.state` and a callback that calls `this.setState()`.
|
||||
正如你所见,`ReactLink`对象是非常简单,只有`value`和`requestChange`属性.并且`LinkStateMixin`也很简单:它只是作用(populates)于`this.state`的元素值并且回调名为`this.setState()`的函数.
|
||||
|
||||
### ReactLink Without valueLink
|
||||
|
||||
@@ -114,4 +114,4 @@ var WithoutLink = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
The `valueLink` prop is also quite simple. It simply handles the `onChange` event and calls `this.props.valueLink.requestChange()` and also uses `this.props.valueLink.value` instead of `this.props.value`. That's it!
|
||||
对于`valueLink`的属性同样也很简单,它只是简单的处理`onChange`事件,调用`this.props.valueLink.requestChange()`的时候也使用`this.props.valueLink.requestChange()`代替`this.props.value`.这就是双向绑定!
|
||||
|
||||
@@ -30,7 +30,7 @@ React.addons.TestUtils.Simulate.click(node);
|
||||
|
||||
```javascript
|
||||
var node = ReactDOM.findDOMNode(this.refs.input);
|
||||
node.value = 'giraffe';
|
||||
node.value = 'giraffe'
|
||||
React.addons.TestUtils.Simulate.change(node);
|
||||
React.addons.TestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@ ReactTestUtils.Simulate.click(node);
|
||||
```javascript
|
||||
// <input ref="input" />
|
||||
var node = this.refs.input;
|
||||
node.value = 'giraffe';
|
||||
node.value = 'giraffe'
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@ ReactTestUtils.Simulate.click(node);
|
||||
```javascript
|
||||
// <input ref="input" />
|
||||
var node = this.refs.input;
|
||||
node.value = 'giraffe';
|
||||
node.value = 'giraffe'
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
|
||||
```
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
---
|
||||
id: test-utils-zh-CN
|
||||
title: 测试工具
|
||||
permalink: test-utils-zh-CN.html
|
||||
prev: two-way-binding-helpers-zh-CN.html
|
||||
next: clone-with-props-zh-CN.html
|
||||
---
|
||||
|
||||
`ReactTestUtils` 使你在你选择的测试框架中 (我们使用 [Jest](https://facebook.github.io/jest/)) 测试 React 组件变得容易。
|
||||
|
||||
```
|
||||
var ReactTestUtils = require('react-addons-test-utils');
|
||||
```
|
||||
|
||||
### Simulate
|
||||
|
||||
```javascript
|
||||
Simulate.{eventName}(
|
||||
DOMElement element,
|
||||
[object eventData]
|
||||
)
|
||||
```
|
||||
|
||||
模拟一个在 DOM 节点上带有可选 `eventData` 事件数据的事件派遣(event dispatch)。**这可能是 `ReactTestUtils` 里单独最有用的工具。**
|
||||
|
||||
**点击一个元素**
|
||||
|
||||
```javascript
|
||||
// <button ref="button">...</button>
|
||||
var node = this.refs.button;
|
||||
ReactTestUtils.Simulate.click(node);
|
||||
```
|
||||
|
||||
**改变 input 域的值然后点击 回车。**
|
||||
|
||||
```javascript
|
||||
// <input ref="input" />
|
||||
var node = this.refs.input;
|
||||
node.value = 'giraffe';
|
||||
ReactTestUtils.Simulate.change(node);
|
||||
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});
|
||||
```
|
||||
|
||||
*注意你将必须提供任何你在你的组件里使用的事件属性(例如 keyCode, which, 等等)因为React没有为你创建任何这类东西。*
|
||||
|
||||
`Simulate` has a method for [every event that React understands](/react/docs/events.html#supported-events).
|
||||
|
||||
### renderIntoDocument
|
||||
|
||||
```javascript
|
||||
ReactComponent renderIntoDocument(
|
||||
ReactElement instance
|
||||
)
|
||||
```
|
||||
|
||||
渲染一个组件到 document 里的 detached DOM 节点。**这个函数需要一个 DOM。**
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 在你 import React **之前**,你需要让 `window`, `window.document` 和 `window.document.createElement` 全局可用。
|
||||
不然 React 会认为它不能访问 DOM 然后类似 `setState` 的方法会不工作。
|
||||
|
||||
### mockComponent
|
||||
|
||||
```javascript
|
||||
object mockComponent(
|
||||
function componentClass,
|
||||
[string mockTagName]
|
||||
)
|
||||
```
|
||||
|
||||
传入一个 mocked 组件模块到这个方法来给它增加有用的方法,使它可以被用作 dummy React 组件。代替像通常一样的渲染,组件会成为一个简单的包含了任意被提供的子级的 `<div>` (或者其他 tag 名,如果提供了 `mockTagName`)
|
||||
|
||||
### isElement
|
||||
|
||||
```javascript
|
||||
boolean isElement(
|
||||
ReactElement element
|
||||
)
|
||||
```
|
||||
|
||||
返回 `true` 如果 `element` 是任意的 ReactElement。
|
||||
|
||||
### isElementOfType
|
||||
|
||||
```javascript
|
||||
boolean isElementOfType(
|
||||
ReactElement element,
|
||||
function componentClass
|
||||
)
|
||||
```
|
||||
|
||||
返回 `true` 如果 `element` 是一个类型是 React `componentClass` 的 ReactElement。
|
||||
|
||||
### isDOMComponent
|
||||
|
||||
```javascript
|
||||
boolean isDOMComponent(
|
||||
ReactComponent instance
|
||||
)
|
||||
```
|
||||
|
||||
返回 `true` 如果 `instance` 是一个 DOM 组件 (比如一个 `<div>` 或者 `<span>`)。
|
||||
|
||||
### isCompositeComponent
|
||||
|
||||
```javascript
|
||||
boolean isCompositeComponent(
|
||||
ReactComponent instance
|
||||
)
|
||||
```
|
||||
|
||||
返回 `true` 如果 `instance` 是一个复合组件 (由 `React.createClass()` 创建)。
|
||||
|
||||
### isCompositeComponentWithType
|
||||
|
||||
```javascript
|
||||
boolean isCompositeComponentWithType(
|
||||
ReactComponent instance,
|
||||
function componentClass
|
||||
)
|
||||
```
|
||||
|
||||
返回 `true` 如果 `instance` 是一个类型为 React `componentClass` 的复合组件 (由 `React.createClass()` 创建)。
|
||||
|
||||
### findAllInRenderedTree
|
||||
|
||||
```javascript
|
||||
array findAllInRenderedTree(
|
||||
ReactComponent tree,
|
||||
function test
|
||||
)
|
||||
```
|
||||
|
||||
遍历 `tree` 里所有的组件,并累积所有 `test(component)` 为 `true` 的组件。它本身并没有什么用,但是它被用作其他测试工具的基本元素。
|
||||
|
||||
### scryRenderedDOMComponentsWithClass
|
||||
|
||||
```javascript
|
||||
array scryRenderedDOMComponentsWithClass(
|
||||
ReactComponent tree, string className
|
||||
)
|
||||
```
|
||||
|
||||
在渲染的树中查找所有 DOM组件的类名匹配`className` 的组件实例。
|
||||
|
||||
### findRenderedDOMComponentWithClass
|
||||
|
||||
```javascript
|
||||
ReactComponent findRenderedDOMComponentWithClass(
|
||||
ReactComponent tree,
|
||||
string className
|
||||
)
|
||||
```
|
||||
|
||||
类似 `scryRenderedDOMComponentsWithClass()` 除了只有一个返回结果,并且要么返回这个结果,要么如果还有其他的匹配项就抛出一个异常。
|
||||
|
||||
### scryRenderedDOMComponentsWithTag
|
||||
|
||||
```javascript
|
||||
array scryRenderedDOMComponentsWithTag(
|
||||
ReactComponent tree,
|
||||
string tagName
|
||||
)
|
||||
```
|
||||
|
||||
在渲染的树中查找所有 DOM 组件的 tag 名匹配 `tagName` 的组件实例。
|
||||
|
||||
### findRenderedDOMComponentWithTag
|
||||
|
||||
```javascript
|
||||
ReactComponent findRenderedDOMComponentWithTag(
|
||||
ReactComponent tree,
|
||||
string tagName
|
||||
)
|
||||
```
|
||||
|
||||
类似 `scryRenderedDOMComponentsWithTag()` 除了只有一个返回结果,并且要么返回这个结果,要么如果还有其他的匹配项就抛出一个异常。
|
||||
|
||||
### scryRenderedComponentsWithType
|
||||
|
||||
```javascript
|
||||
array scryRenderedComponentsWithType(
|
||||
ReactComponent tree,
|
||||
function componentClass
|
||||
)
|
||||
```
|
||||
|
||||
查找所有类型等于 `componentClass` 的组件实例。
|
||||
|
||||
### findRenderedComponentWithType
|
||||
|
||||
```javascript
|
||||
ReactComponent findRenderedComponentWithType(
|
||||
ReactComponent tree, function componentClass
|
||||
)
|
||||
```
|
||||
|
||||
类似 `scryRenderedComponentsWithType()` 除了只有一个返回结果,并且要么返回这个结果,要么如果还有其他的匹配项就抛出一个异常。
|
||||
|
||||
## Shallow rendering(浅渲染)
|
||||
|
||||
浅渲染是一个实验性特性,让你渲染一个组件为 "one level deep" 并且断言渲染方法返回的内容,不用担心子组件的行为,它们没有被实例化或者渲染。这个方式不需要一个 DOM。
|
||||
|
||||
```javascript
|
||||
ReactShallowRenderer createRenderer()
|
||||
```
|
||||
|
||||
在你的测试里调用它来创建一个浅渲染器。你可以把它想做是一个你渲染你要测试的组件的 "地方",它可以自己响应事件并更新。
|
||||
|
||||
```javascript
|
||||
shallowRenderer.render(
|
||||
ReactElement element
|
||||
)
|
||||
```
|
||||
|
||||
类似于 `ReactDOM.render`。
|
||||
|
||||
```javascript
|
||||
ReactElement shallowRenderer.getRenderOutput()
|
||||
```
|
||||
|
||||
在 `render` 被调用后,返回一个浅渲染的输出。你可以接着断言输出的内容。例如,如果你的组件的渲染方法返回:
|
||||
|
||||
```javascript
|
||||
<div>
|
||||
<span className="heading">Title</span>
|
||||
<Subcomponent foo="bar" />
|
||||
</div>
|
||||
```
|
||||
|
||||
然后你可以断言:
|
||||
|
||||
```javascript
|
||||
var renderer = ReactTestUtils.createRenderer();
|
||||
result = renderer.getRenderOutput();
|
||||
expect(result.type).toBe('div');
|
||||
expect(result.props.children).toEqual([
|
||||
<span className="heading">Title</span>,
|
||||
<Subcomponent foo="bar" />
|
||||
]);
|
||||
```
|
||||
|
||||
浅测试现在有一些限制,即不支持 refs。我们在早期发布这个特性,并感激 React 社区关于它应该如何演化的反馈。
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
id: clone-with-props-zh-CN
|
||||
title: 克隆 ReactElements
|
||||
permalink: clone-with-props-zh-CN.html
|
||||
prev: test-utils-zh-CN.html
|
||||
next: create-fragment-zh-CN.html
|
||||
---
|
||||
|
||||
> 注意:
|
||||
> `cloneWithProps` 被弃用了. 用 [React.cloneElement](top-level-api.html#react.cloneelement) 代替.
|
||||
|
||||
在很罕见的情况下,你可能需要创建一个 React 元素的拷贝,它与初始的元素有不同的 props。一个例子是克隆这些传递到 `this.props.children` 的元素并用不同的 props 渲染他们。
|
||||
|
||||
```js
|
||||
var cloneWithProps = require('react-addons-clone-with-props');
|
||||
|
||||
var _makeBlue = function(element) {
|
||||
return cloneWithProps(element, {style: {color: 'blue'}});
|
||||
};
|
||||
|
||||
var Blue = React.createClass({
|
||||
render: function() {
|
||||
var blueChildren = React.Children.map(this.props.children, _makeBlue);
|
||||
return <div>{blueChildren}</div>;
|
||||
}
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<Blue>
|
||||
<p>This text is blue.</p>
|
||||
</Blue>,
|
||||
document.getElementById('container')
|
||||
);
|
||||
```
|
||||
|
||||
`cloneWithProps` 不传递 `key` 或者 `ref` 到被克隆的元素。`className` 和 `style` props 被自动合并。
|
||||
@@ -1,75 +0,0 @@
|
||||
---
|
||||
id: create-fragment-zh-CN
|
||||
title: Keyed Fragments
|
||||
permalink: create-fragment-zh-CN.html
|
||||
prev: clone-with-props-zh-CN.html
|
||||
next: update-zh-CN.html
|
||||
---
|
||||
|
||||
在大多数情况下,你可以使用 `key` prop 指定你从 `render` 返回的元素的 keys。然而,这在一个情况下会失败:如果你有两组你需要记录的子级,将没有办法在不使用包裹元素的情况下放置一个 key 到每组上。
|
||||
|
||||
即是,如果你有一个像这样的组件:
|
||||
|
||||
```js
|
||||
var Swapper = React.createClass({
|
||||
propTypes: {
|
||||
// `leftChildren` and `rightChildren` can be a string, element, array, etc.
|
||||
leftChildren: React.PropTypes.node,
|
||||
rightChildren: React.PropTypes.node,
|
||||
|
||||
swapped: React.PropTypes.bool
|
||||
},
|
||||
render: function() {
|
||||
var children;
|
||||
if (this.props.swapped) {
|
||||
children = [this.props.rightChildren, this.props.leftChildren];
|
||||
} else {
|
||||
children = [this.props.leftChildren, this.props.rightChildren];
|
||||
}
|
||||
return <div>{children}</div>;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
这些子级会在当你改变 `swapped` prop 时加载和卸载,因为没有任何的 key 标记在这两组子级上。
|
||||
|
||||
要解决这个问题,你可以使用 `createFragment` 插件来给予这两组子级 keys.
|
||||
|
||||
#### `Array<ReactNode> createFragment(object children)`
|
||||
|
||||
代替创建数组,我们这样写:
|
||||
|
||||
```js
|
||||
var createFragment = require('react-addons-create-fragment');
|
||||
|
||||
if (this.props.swapped) {
|
||||
children = createFragment({
|
||||
right: this.props.rightChildren,
|
||||
left: this.props.leftChildren
|
||||
});
|
||||
} else {
|
||||
children = createFragment({
|
||||
left: this.props.leftChildren,
|
||||
right: this.props.rightChildren
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
被传入对象的 keys (即 `left` 和 `right`)被用作为整组子级的 keys,并且对象 keys 的顺序被用于决定渲染子级的顺序。通过这个改变,这两个子级将会恰当的在 DOM 里排序,而不被卸载。
|
||||
|
||||
`createFragment` 的返回值应该被对待为一个不透明的对象;你可以使用 `React.Children` 来遍历一个 fragment 但是不应该直接访问它。同样注意,我们依赖于 JavaScript 引擎保留了对象的枚举顺序,这点在 spec 上是不保证的,但是所有主要的浏览器和 VMs 都对非数字键的对象实现了这个特性。
|
||||
|
||||
> **注意:**
|
||||
>
|
||||
> 将来,`createFragment` 也许会被替换为如下的API:
|
||||
>
|
||||
> ```js
|
||||
> return (
|
||||
> <div>
|
||||
> <x:frag key="right">{this.props.rightChildren}</x:frag>,
|
||||
> <x:frag key="left">{this.props.leftChildren}</x:frag>
|
||||
> </div>
|
||||
> );
|
||||
> ```
|
||||
>
|
||||
> 允许你直接在 JSX 里赋值 keys 而不用添加包裹元素。
|
||||
@@ -1,102 +0,0 @@
|
||||
---
|
||||
id: update-zh-CN
|
||||
title: immutability 助手
|
||||
permalink: update-zh-CN.html
|
||||
prev: create-fragment-zh-CN.html
|
||||
next: pure-render-mixin-zh-CN.html
|
||||
---
|
||||
|
||||
React 让你可以使用任何你想要的数据管理方式,包括 mutation。然而,如果你可以在你的应用的性能关键性部分里使用 immutable 数据,将会易于实现一个快速的 `shouldComponentUpdate()` 方法来显著加速你的 app。
|
||||
|
||||
在 JavaScript 里处理 immutable 数据比在为此设计的语言中要难的多,比如 [Clojure](http://clojure.org/)。然而,我们提供了一个简单的 immutability 助手,`update()`,它使处理这类数据容易多了,*不用* 根本性的改变你的数据的表达方式。你也同样可以看一看 Facebook 的 [Immutable-js](https://facebook.github.io/immutable-js/docs/) 和[Advanced Performance](/react/docs/advanced-performance.html) 了解更多关于 Immutable-js 的信息。
|
||||
|
||||
## 主要的思路
|
||||
|
||||
如果你像这样变动数据:
|
||||
|
||||
```js
|
||||
myData.x.y.z = 7;
|
||||
// or...
|
||||
myData.a.b.push(9);
|
||||
```
|
||||
|
||||
你将没有任何办法决定哪个数据被改变了,因为之前的拷贝已经被覆盖。作为替代,你需要创建一个新的 `myData` 的拷贝并且只修改需要改变的地方。然后你可以用三个等于在 `shouldComponentUpdate()` 里比较旧的 `myData` 拷贝与新的拷贝:
|
||||
|
||||
```js
|
||||
var newData = deepCopy(myData);
|
||||
newData.x.y.z = 7;
|
||||
newData.a.b.push(9);
|
||||
```
|
||||
|
||||
不幸的是,深拷贝很昂贵,并且有时候不可能。你可以通过仅仅拷贝需要被改变和重用没有改变的对象来缓解这个情况。不幸的是,在当今的 JavaScript 里这会很笨重:
|
||||
|
||||
```js
|
||||
var newData = extend(myData, {
|
||||
x: extend(myData.x, {
|
||||
y: extend(myData.x.y, {z: 7}),
|
||||
}),
|
||||
a: extend(myData.a, {b: myData.a.b.concat(9)})
|
||||
});
|
||||
```
|
||||
|
||||
虽然这相当高性能(因为只对 `log n` 的对象进行了浅拷贝并重用了剩下的),但它写起来很痛苦。看看所有重复的代码!这不仅仅是烦人的,同时也提供了一大片 bugs 区域。
|
||||
|
||||
`update()` 提供了这个模式的简单语法糖来使写这类代码更容易。上面的代码变成:
|
||||
|
||||
```js
|
||||
var update = require('react-addons-update');
|
||||
|
||||
var newData = update(myData, {
|
||||
x: {y: {z: {$set: 7}}},
|
||||
a: {b: {$push: [9]}}
|
||||
});
|
||||
```
|
||||
|
||||
虽然这个语法需要花一些时间来适应(它的灵感来自于 [MongoDB's query language](http://docs.mongodb.org/manual/core/crud-introduction/#query)),但是没有冗余,它可静态分析并且没有 mutative 版本那么多键入。
|
||||
|
||||
`$`-前缀的 keys 被称为 *命令*。被 "变动的" 数据结构被称为 *目标*。
|
||||
|
||||
## 有效的命令
|
||||
|
||||
* `{$push: array}` 在目标上 `push()` 所有 `array` 里的项目。
|
||||
* `{$unshift: array}` 在目标上 `unshift()` 所有 `array` 里的项目。
|
||||
* `{$splice: array of arrays}` 在目标上对于每一个 `arrays` 里的项目使用项目提供的参数调用 `splice()`。
|
||||
* `{$set: any}` 整个替换目标.
|
||||
* `{$merge: object}` 合并 目标和 `object` 的 keys.
|
||||
* `{$apply: function}` 传递当前的值给 function 并用返回值更新它。
|
||||
|
||||
## 例子
|
||||
|
||||
### 简单的 push
|
||||
|
||||
```js
|
||||
var initialArray = [1, 2, 3];
|
||||
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]
|
||||
```
|
||||
`initialArray` is still `[1, 2, 3]`.
|
||||
|
||||
### 嵌套的 collections
|
||||
|
||||
```js
|
||||
var collection = [1, 2, {a: [12, 17, 15]}];
|
||||
var newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
|
||||
// => [1, 2, {a: [12, 13, 14, 15]}]
|
||||
```
|
||||
本例访问了 `collection` 的`2`索引下的键`a`,并且拼接了一个从索引`1`开始(移除`17`)并插入`13`和`14`的项目。
|
||||
|
||||
### 基于当前的值更新新值
|
||||
|
||||
```js
|
||||
var obj = {a: 5, b: 3};
|
||||
var newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
|
||||
// => {a: 5, b: 6}
|
||||
// This is equivalent, but gets verbose for deeply nested collections:
|
||||
var newObj2 = update(obj, {b: {$set: obj.b * 2}});
|
||||
```
|
||||
|
||||
### (浅) 合并
|
||||
|
||||
```js
|
||||
var obj = {a: 5, b: 3};
|
||||
var newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}
|
||||
```
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
id: pure-render-mixin-zh-CN
|
||||
title: PureRenderMixin
|
||||
permalink: pure-render-mixin-zh-CN.html
|
||||
prev: update-zh-CN.html
|
||||
next: perf-zh-CN.html
|
||||
---
|
||||
|
||||
如果你的 React 组件的 render 函数是 "纯净" 的(换句话说,它的渲染在给定相同的 props 和 state 时返回相同的结果),你可以使用这个 mixin 在某些情况下进行性能加速。
|
||||
|
||||
例子:
|
||||
|
||||
```js
|
||||
var PureRenderMixin = require('react-addons-pure-render-mixin');
|
||||
React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div className={this.props.className}>foo</div>;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
使用 ES6 class 语法的例子:
|
||||
|
||||
```js
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
class FooComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className={this.props.className}>foo</div>;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在内部, mixin 实现了 [shouldComponentUpdate](/react/docs/component-specs.html#updating-shouldcomponentupdate), 它对当前及下一步的 props 和 state 进行比较,并当相等时返回 `false`。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 它仅仅浅比较对象。如果包含了复杂的对象,可能会对于深层的不同产生 false-negatives。只 mix 到那些含有简单 props 和 state 的组件,或者在你知道深层对象改变时使用 `forceUpdate()` 。或者,考虑使用[immutable objects](https://facebook.github.io/immutable-js/) 来促进快速的嵌套数据比较。
|
||||
>
|
||||
> 此外, `shouldComponentUpdate` 跳过了整个子树的更新。确保所有的子组件都是 "纯净" 的。
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
id: perf-zh-CN
|
||||
title: 性能工具
|
||||
permalink: perf-zh-CN.html
|
||||
prev: pure-render-mixin-zh-CN.html
|
||||
next: shallow-compare-zh-CN.html
|
||||
---
|
||||
|
||||
React 通常是相当快的。然而,在你需要压榨你的 app 的每一分性能的情况下,它提供了一个[shouldComponentUpdate](/react/docs/component-specs.html#updating-shouldcomponentupdate) 钩子,在此你可以添加优化提示到 React 的 diff 算法里。
|
||||
|
||||
除了给予你一个你的 app 的整体性能概览外,ReactPerf 还是一个准确告诉你,你需要在哪里放置这些钩子的分析工具。
|
||||
|
||||
## General API
|
||||
|
||||
这里陈述的 `Perf` 对象被用 `require('react-addons-perf')` 暴露,并且只能被使用在 React 的开发模式。你不应在生产环境下在你的 app 包含这个包。
|
||||
|
||||
> 注意:
|
||||
>
|
||||
> 开发版的 React 慢于生产版,因为所有额外提供的逻辑,例如,React 的友好的控制台警告 (在生产版中被除去)。因此,分析工具仅服务于指示你的 app _相对_ 昂贵的部分。
|
||||
|
||||
### `Perf.start()` and `Perf.stop()`
|
||||
开始/停止测量。其间的React操作被记录用于之后的分析。产生无关紧要时间的操作被忽略。
|
||||
|
||||
在停止以后,你将需要 `Perf.getLastMeasurements()` (下面将介绍)来获取测量结果。
|
||||
|
||||
### `Perf.printInclusive(measurements)`
|
||||
打印总体花费的时间。如果没有传入参数,默认是从上次记录的所有测量数据。它在控制台里打印良好格式化的结果,像这样:
|
||||
|
||||

|
||||
|
||||
### `Perf.printExclusive(measurements)`
|
||||
"独占的"时间不包括花费于加载组件的时间: 处理 props, `getInitialState`, 调用 `componentWillMount` 及 `componentDidMount`, 等等。
|
||||
|
||||

|
||||
|
||||
### `Perf.printWasted(measurements)`
|
||||
|
||||
**分析工具里最有用的部分**.
|
||||
|
||||
"垃圾"时间是花费在组件上实际没有绘制任何东西的时间,例如渲染结果总是相同,所以 DOM 没有被触碰到。
|
||||
|
||||

|
||||
|
||||
### `Perf.printDOM(measurements)`
|
||||
打印底层的 DOM 操纵,例如 "set innerHTML" 和 "remove".
|
||||
|
||||

|
||||
|
||||
## Advanced API
|
||||
|
||||
以上的打印方法使用 `Perf.getLastMeasurements()` 来美观的打印结果。
|
||||
|
||||
### `Perf.getLastMeasurements()`
|
||||
从最后的 start-stop 会话获取测量数据数据。这个数组包含对象,每个看起来像这样:
|
||||
|
||||
```js
|
||||
{
|
||||
// The term "inclusive" and "exclusive" are explained below
|
||||
"exclusive": {},
|
||||
// '.0.0' is the React ID of the node
|
||||
"inclusive": {".0.0": 0.0670000008540228, ".0": 0.3259999939473346},
|
||||
"render": {".0": 0.036999990697950125, ".0.0": 0.010000003385357559},
|
||||
// Number of instances
|
||||
"counts": {".0": 1, ".0.0": 1},
|
||||
// DOM touches
|
||||
"writes": {},
|
||||
// Extra debugging info
|
||||
"displayNames": {
|
||||
".0": {"current": "App", "owner": "<root>"},
|
||||
".0.0": {"current": "Box", "owner": "App"}
|
||||
},
|
||||
"totalTime": 0.48499999684281647
|
||||
}
|
||||
```
|
||||
@@ -177,7 +177,7 @@ var MediaQuery = React.createClass({
|
||||
},
|
||||
componentDidMount: function(){
|
||||
var checkMediaQuery = function(){
|
||||
var type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
|
||||
var type = window.matchMedia("min-width: 1025px").matches ? 'desktop' : 'mobile';
|
||||
if (type !== this.state.type){
|
||||
this.setState({type:type});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ permalink: context-zh-CN.html
|
||||
prev: advanced-performance-zh-CN.html
|
||||
---
|
||||
|
||||
React最大的优势之一是很容易从你的React组件里跟踪数据流动。当你看着一个组件,你可以很容易准确看出哪个props被传入,这让你的APP很容易推断。
|
||||
React最大的优势之一是他很容易从你的React组件里跟踪数据流动。当你看着一个组件,你可以很容易准确看出哪个props被传入,这让你的APP很容易推断。
|
||||
|
||||
偶尔,你想通过组件树传递数据,而不在每一级上手工下传prop,React的 "context" 让你做到这点。
|
||||
|
||||
@@ -160,38 +160,6 @@ function Button(props, context) {
|
||||
Button.contextTypes = {color: React.PropTypes.string};
|
||||
```
|
||||
|
||||
## Updating context
|
||||
|
||||
当 state 或者 props 变化时 `getChildContext` 函数会被调用。为了更新context里的数据,用 `this.setState` 触发一个本地的state更新。这将会触发一个新的 context 并且子级将收到变化。
|
||||
|
||||
```javascript
|
||||
var MediaQuery = React.createClass({
|
||||
getInitialState: function(){
|
||||
return {type:'desktop'};
|
||||
},
|
||||
childContextTypes: {
|
||||
type: React.PropTypes.string
|
||||
},
|
||||
getChildContext: function() {
|
||||
return {type: this.state.type};
|
||||
},
|
||||
componentDidMount: function(){
|
||||
var checkMediaQuery = function(){
|
||||
var type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
|
||||
if (type !== this.state.type){
|
||||
this.setState({type:type});
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', checkMediaQuery);
|
||||
checkMediaQuery();
|
||||
},
|
||||
render: function(){
|
||||
return this.props.children;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 什么时候不用 context
|
||||
|
||||
正像全局变量是在写清晰代码时最好要避免的,你应该在大多数情况下避免使用context. 特别是,在用它来"节省输入"和代替显示传入props时要三思.
|
||||
|
||||
@@ -46,15 +46,6 @@ $ webpack
|
||||
>
|
||||
> 如果你正在使用 ES2015, 你将要使用 `babel-preset-es2015` 包.
|
||||
|
||||
**注意:** 默认情况下,React 将会在开发模式,很缓慢,不建议用于生产。要在生产模式下使用 React,设置环境变量 `NODE_ENV` 为 `production` (使用 envify 或者 webpack's DefinePlugin)。例如:
|
||||
|
||||
```js
|
||||
new webpack.DefinePlugin({
|
||||
"process.env": {
|
||||
NODE_ENV: JSON.stringify("production")
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 不用 npm 的快速开始
|
||||
|
||||
@@ -90,7 +81,7 @@ new webpack.DefinePlugin({
|
||||
</html>
|
||||
```
|
||||
|
||||
在 JavaScript 代码里写着 XML 格式的代码称为 JSX;可以去 [JSX 语法](/react/docs/jsx-in-depth.html) 里学习更多 JSX 相关的知识。为了把 JSX 转成标准的 JavaScript,我们用 `<script type="text/babel">` 标签,并引入 Babel 来完成在浏览器里的代码转换。在浏览器里打开这个html,你应该可以看到成功的消息!
|
||||
在 JavaScript 代码里写着 XML 格式的代码称为 JSX;可以去 [JSX 语法](/react/docs/jsx-in-depth.html) 里学习更多 JSX 相关的知识。为了把 JSX 转成标准的 JavaScript,我们用 `<script type="text/babel">` 标签,并引入 Babel 来完成在浏览器里的代码转换。
|
||||
|
||||
### 分离文件
|
||||
|
||||
@@ -139,7 +130,6 @@ ReactDOM.render(
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
对照下面更新你的 HTML 代码
|
||||
|
||||
```html{8,12}
|
||||
|
||||
@@ -86,13 +86,6 @@ There is also the React-specific attribute `dangerouslySetInnerHTML` ([more here
|
||||
|
||||
### SVG Attributes
|
||||
|
||||
```
|
||||
clipPath cx cy d dx dy fill fillOpacity fontFamily
|
||||
fontSize fx fy gradientTransform gradientUnits markerEnd
|
||||
markerMid markerStart offset opacity patternContentUnits
|
||||
patternUnits points preserveAspectRatio r rx ry spreadMethod
|
||||
stopColor stopOpacity stroke strokeDasharray strokeLinecap
|
||||
strokeOpacity strokeWidth textAnchor transform version
|
||||
viewBox x1 x2 x xlinkActuate xlinkArcrole xlinkHref xlinkRole
|
||||
xlinkShow xlinkTitle xlinkType xmlBase xmlLang xmlSpace y1 y2 y
|
||||
```
|
||||
Any attributes passed to SVG tags are passed through without changes.
|
||||
|
||||
React used to support special camelCase aliases for certain SVG attributes, such as `clipPath`. If you use them now you'll see a deprecation warning. These aliases will be removed in the next version in favor of their real names from the SVG specification, such as `clip-path`. Attributes that have a camelCase name in the spec, such as `gradientTransform`, will keep their names.
|
||||
|
||||
@@ -18,11 +18,11 @@ React试着支持所有常见的元素.如果你需要一个没有列在这里
|
||||
a abbr address area article aside audio b base bdi bdo big blockquote body br
|
||||
button canvas caption cite code col colgroup data datalist dd del details dfn
|
||||
dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5
|
||||
h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li
|
||||
link main map mark menu menuitem meta meter nav noscript object ol optgroup
|
||||
option output p param picture pre progress q rp rt ruby s samp script section
|
||||
select small source span strong style sub summary sup table tbody td textarea
|
||||
tfoot th thead time title tr track u ul var video wbr
|
||||
h6 head header hr html i iframe img input ins kbd keygen label legend li link
|
||||
main map mark menu menuitem meta meter nav noscript object ol optgroup option
|
||||
output p param picture pre progress q rp rt ruby s samp script section select
|
||||
small source span strong style sub summary sup table tbody td textarea tfoot th
|
||||
thead time title tr track u ul var video wbr
|
||||
```
|
||||
|
||||
### SVG 元素
|
||||
@@ -30,8 +30,8 @@ tfoot th thead time title tr track u ul var video wbr
|
||||
下面的 SVG 元素是被支持的:
|
||||
|
||||
```
|
||||
circle clipPath defs ellipse g image line linearGradient mask path pattern
|
||||
polygon polyline radialGradient rect stop svg text tspan
|
||||
circle clipPath defs ellipse g line linearGradient mask path pattern polygon polyline
|
||||
radialGradient rect stop svg text tspan
|
||||
```
|
||||
|
||||
你也许对 [react-art](https://github.com/facebook/react-art)有兴趣,一个让React绘制Canvas, SVG, 或者 VML (for IE8) 的绘制库.
|
||||
@@ -53,33 +53,25 @@ React支持所有的 `data-*` 和 `aria-*` 以及下列的属性.
|
||||
|
||||
```
|
||||
accept acceptCharset accessKey action allowFullScreen allowTransparency alt
|
||||
async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
|
||||
charSet checked classID className colSpan cols content contentEditable
|
||||
contextMenu controls coords crossOrigin data dateTime default defer dir
|
||||
disabled download draggable encType form formAction formEncType formMethod
|
||||
formNoValidate formTarget frameBorder headers height hidden high href hrefLang
|
||||
htmlFor httpEquiv icon id inputMode integrity is keyParams keyType kind label
|
||||
lang list loop low manifest marginHeight marginWidth max maxLength media
|
||||
mediaGroup method min minLength multiple muted name noValidate nonce open
|
||||
optimum pattern placeholder poster preload radioGroup readOnly rel required
|
||||
reversed role rowSpan rows sandbox scope scoped scrolling seamless selected
|
||||
shape size sizes span spellCheck src srcDoc srcLang srcSet start step style
|
||||
summary tabIndex target title type useMap value width wmode wrap
|
||||
```
|
||||
|
||||
这些RDFa属性是被支持的 (许多RDFa属性和标准的HTML属性重叠,因此被从这个列表里去除):
|
||||
|
||||
```
|
||||
about datatype inlist prefix property resource typeof vocab
|
||||
async autoComplete autoFocus autoPlay capture cellPadding cellSpacing charSet
|
||||
challenge checked classID className cols colSpan content contentEditable contextMenu
|
||||
controls coords crossOrigin data dateTime defer dir disabled download draggable
|
||||
encType form formAction formEncType formMethod formNoValidate formTarget frameBorder
|
||||
headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode
|
||||
keyParams keyType label lang list loop low manifest marginHeight marginWidth max
|
||||
maxLength media mediaGroup method min minLength multiple muted name noValidate open
|
||||
optimum pattern placeholder poster preload radioGroup readOnly rel required role
|
||||
rows rowSpan sandbox scope scoped scrolling seamless selected shape size sizes
|
||||
span spellCheck src srcDoc srcSet start step style summary tabIndex target title
|
||||
type useMap value width wmode wrap
|
||||
```
|
||||
|
||||
另外,支持下面的非标准属性:
|
||||
|
||||
- `autoCapitalize autoCorrect` for Mobile Safari.
|
||||
- `color` for `<link rel="mask-icon" />` in Safari.
|
||||
- `property` for [Open Graph](http://ogp.me/) meta tags.
|
||||
- `itemProp itemScope itemType itemRef itemID` for [HTML5 microdata](http://schema.org/docs/gs.html).
|
||||
- `security` 老的IE浏览器.
|
||||
- `unselectable` IE浏览器.
|
||||
- `unselectable` for Internet Explorer.
|
||||
- `results autoSave` for WebKit/Blink input fields of type `search`.
|
||||
|
||||
同样有React规范的属性 `dangerouslySetInnerHTML` ([more here](/react/docs/special-non-dom-attributes.html)),用于直接插入HTML字符串到组件里.
|
||||
|
||||
@@ -137,6 +137,8 @@ Properties:
|
||||
DOMEventTarget relatedTarget
|
||||
```
|
||||
|
||||
These focus events work on all elements in the React DOM, not just form elements.
|
||||
|
||||
|
||||
### Form Events
|
||||
|
||||
@@ -245,6 +247,7 @@ number deltaY
|
||||
number deltaZ
|
||||
```
|
||||
|
||||
|
||||
### Media Events
|
||||
|
||||
Event names:
|
||||
@@ -253,6 +256,7 @@ Event names:
|
||||
onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
|
||||
```
|
||||
|
||||
|
||||
### Image Events
|
||||
|
||||
Event names:
|
||||
@@ -260,3 +264,37 @@ Event names:
|
||||
```
|
||||
onLoad onError
|
||||
```
|
||||
|
||||
|
||||
### Animation Events
|
||||
|
||||
Event names:
|
||||
|
||||
```
|
||||
onAnimationStart onAnimationEnd onAnimationIteration
|
||||
```
|
||||
|
||||
Properties:
|
||||
|
||||
```javascript
|
||||
string animationName
|
||||
string pseudoElement
|
||||
float elapsedTime
|
||||
```
|
||||
|
||||
|
||||
### Transition Events
|
||||
|
||||
Event names:
|
||||
|
||||
```
|
||||
onTransitionEnd
|
||||
```
|
||||
|
||||
Properties:
|
||||
|
||||
```javascript
|
||||
string propertyName
|
||||
string pseudoElement
|
||||
float elapsedTime
|
||||
```
|
||||
|
||||
@@ -139,7 +139,6 @@ DOMEventTarget relatedTarget
|
||||
|
||||
焦点事件在所有的React DOM上工作,不仅仅是表单元素.
|
||||
|
||||
|
||||
### 表单事件
|
||||
|
||||
事件名称:
|
||||
@@ -247,8 +246,7 @@ number deltaY
|
||||
number deltaZ
|
||||
```
|
||||
|
||||
|
||||
### 媒体事件
|
||||
### Media Events
|
||||
|
||||
事件名称:
|
||||
|
||||
@@ -256,45 +254,10 @@ number deltaZ
|
||||
onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
|
||||
```
|
||||
|
||||
|
||||
### 图片事件
|
||||
### Image Events
|
||||
|
||||
事件名称:
|
||||
|
||||
```
|
||||
onLoad onError
|
||||
```
|
||||
|
||||
|
||||
### 动画事件
|
||||
|
||||
事件名称:
|
||||
|
||||
```
|
||||
onAnimationStart onAnimationEnd onAnimationIteration
|
||||
```
|
||||
|
||||
属性:
|
||||
|
||||
```javascript
|
||||
string animationName
|
||||
string pseudoElement
|
||||
float elapsedTime
|
||||
```
|
||||
|
||||
|
||||
### Transition Events
|
||||
|
||||
事件名称:
|
||||
|
||||
```
|
||||
onTransitionEnd
|
||||
```
|
||||
|
||||
属性:
|
||||
|
||||
```javascript
|
||||
string propertyName
|
||||
string pseudoElement
|
||||
float elapsedTime
|
||||
```
|
||||
|
||||
@@ -31,7 +31,7 @@ Our JSON API returns some data that looks like this:
|
||||
];
|
||||
```
|
||||
|
||||
## Step 1: break the UI into a component hierarchy
|
||||
## Step 1: Break the UI into a component hierarchy
|
||||
|
||||
The first thing you'll want to do is to draw boxes around every component (and subcomponent) in the mock and give them all names. If you're working with a designer, they may have already done this, so go talk to them! Their Photoshop layer names may end up being the names of your React components!
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ next: thinking-in-react-zh-CN.html
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/{{site.react_version}}/react-dom.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
@@ -97,7 +96,7 @@ ReactDOM.render(
|
||||
|
||||
#### JSX 语法
|
||||
|
||||
首先你会注意到你的 JavaScript 中 XML 式的语法。我们有一个简单的预编译器,将语法糖转换成这种纯的JavaScript:
|
||||
首先你会注意到你的 JavaScript 中 XML 式的语法。我们有一个简单的预编译器,将这种语法糖转换成单纯的 JavaScript :
|
||||
|
||||
```javascript
|
||||
// tutorial1-raw.js
|
||||
@@ -128,10 +127,6 @@ ReactDOM.render(
|
||||
|
||||
`ReactDOM.render()` 实例化根组件,启动框架,注入标记到原始的 DOM 元素中,作为第二个参数提供。
|
||||
|
||||
`ReactDOM` 模块暴露了 DOM 相关的方法, 而 `React` 保有被不同平台的 React 共享的核心工具 (例如 [React Native](http://facebook.github.io/react-native/))。
|
||||
|
||||
对于本教程 `ReactDOM.render` 保持在脚本底部是很重要的。`ReactDOM.render` 应该只在复合组件被定义之后被调用。
|
||||
|
||||
## 组合组件
|
||||
|
||||
让我们为 `CommentList` 和 `CommentForm` 建造骨架,它们将会,再一次的,是一些简单的 `<div>`。添加这两个组件到你的文件里,保持现存的 `CommentBox` 声明和 `ReactDOM.render` 调用:
|
||||
@@ -224,7 +219,22 @@ var CommentList = React.createClass({
|
||||
|
||||
Markdown 是一种简单的内联格式化你的文字的方法。例如,用星号包围文本将会使其强调突出。
|
||||
|
||||
在本教程中我们使用第三方库 **marked**,它接受 Markdown 文本并且转换为原始的 HTML。我们已经在初始的页面标记里包含了这个库,所以我们可以直接开始使用它,让我们转换评论文本为 Markdown 并输出它:
|
||||
首先,添加第三方库 **marked** 到你的应用。这是一个JavaScript库,接受 Markdown 文本并且转换为原始的 HTML。这需要在你的头部有一个 script 标签(那个我们已经在 React 操场上包含了的标签):
|
||||
|
||||
```html{9}
|
||||
<!-- index.html -->
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>React Tutorial</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/{{site.react_version}}/react.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/{{site.react_version}}/react-dom.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.5/marked.min.js"></script>
|
||||
</head>
|
||||
```
|
||||
|
||||
然后,让我们转换评论文本为 Markdown 并输出它:
|
||||
|
||||
```javascript{9}
|
||||
// tutorial6.js
|
||||
@@ -280,12 +290,12 @@ var Comment = React.createClass({
|
||||
```javascript
|
||||
// tutorial8.js
|
||||
var data = [
|
||||
{id: 1, author: "Pete Hunt", text: "This is one comment"},
|
||||
{id: 2, author: "Jordan Walke", text: "This is *another* comment"}
|
||||
{author: "Pete Hunt", text: "This is one comment"},
|
||||
{author: "Jordan Walke", text: "This is *another* comment"}
|
||||
];
|
||||
```
|
||||
|
||||
我们需要以一种模块化的方式将这个数据传入到 `CommentList`。修改 `CommentBox` 和 `ReactDOM.render()` 方法,以便于通过 props 传入数据到 `CommentList`:
|
||||
我们需要以一种模块化的方式将这个数据传入到 `CommentList`。修改 `CommentBox` 和 `ReactDOM.render()` 方法,以通过 props 传入数据到 `CommentList`:
|
||||
|
||||
```javascript{7,15}
|
||||
// tutorial9.js
|
||||
@@ -313,9 +323,9 @@ ReactDOM.render(
|
||||
// tutorial10.js
|
||||
var CommentList = React.createClass({
|
||||
render: function() {
|
||||
var commentNodes = this.props.data.map(function(comment) {
|
||||
var commentNodes = this.props.data.map(function (comment) {
|
||||
return (
|
||||
<Comment author={comment.author} key={comment.id}>
|
||||
<Comment author={comment.author}>
|
||||
{comment.text}
|
||||
</Comment>
|
||||
);
|
||||
@@ -376,7 +386,7 @@ var CommentBox = React.createClass({
|
||||
`getInitialState()` 在生命周期里只执行一次,并设置组件的初始状态。
|
||||
|
||||
#### 更新状态
|
||||
当组件第一次创建时,我们想从服务器获取一些 JSON 并且更新状态以反映最新的数据。我们将用 jQuery 来发送一个异步请求到我们刚才启动的服务器以获取我们需要的数据。这些数据已经被包含在了你已启动的服务器里(基于`comments.json`文件),所以一旦被获取,`this.state.data` 会看起来像这样:
|
||||
当组件第一次创建时,我们想从服务器获取一些 JSON 并且更新状态以反映最新的数据。我们将用 jQuery 来发送一个异步请求到我们刚才启动的服务器以获取我们需要的数据。看起来像这样:
|
||||
|
||||
```json
|
||||
[
|
||||
@@ -480,39 +490,28 @@ var CommentForm = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
#### 受控组件
|
||||
让我们做一个交互式的表单。当用户提交表单时,我们应该清空它,提交一个请求给服务器,和刷新评论列表。要开始,让我们监听表单的提交事件并清空它。
|
||||
|
||||
对于传统的 DOM, `input` 元素被渲染并且浏览器管理它的状态(它的渲染值)。结果是,DOM的实际值会和组件不同。这是不理想的,因为视图的值会和组件的值不同。在React中,组件应该总是表示视图的值而不只是在初始化时。
|
||||
|
||||
因此,我们将使用 `this.state` 来在用户输入时保存输入。我们定义一个初始 `state`,它带有 `author` 和 `text` 两个属性并将他们设置为空字符串。在我们的 `<input>` 元素里,我们设置 `value` prop 来反映组件的 `state` 并给他们附加 `onChange` 事件处理。这些带有设置了 `value` 的 `<input>` 元素被称为受控组件。更多关于受控组件请阅读 [Forms article](/react/docs/forms.html#controlled-components)。
|
||||
|
||||
```javascript{3-11,15-26}
|
||||
```javascript{3-14,16-19}
|
||||
// tutorial16.js
|
||||
var CommentForm = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {author: '', text: ''};
|
||||
},
|
||||
handleAuthorChange: function(e) {
|
||||
this.setState({author: e.target.value});
|
||||
},
|
||||
handleTextChange: function(e) {
|
||||
this.setState({text: e.target.value});
|
||||
handleSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
var author = React.findDOMNode(this.refs.author).value.trim();
|
||||
var text = React.findDOMNode(this.refs.text).value.trim();
|
||||
if (!text || !author) {
|
||||
return;
|
||||
}
|
||||
// TODO: send request to the server
|
||||
React.findDOMNode(this.refs.author).value = '';
|
||||
React.findDOMNode(this.refs.text).value = '';
|
||||
return;
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<form className="commentForm">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Your name"
|
||||
value={this.state.author}
|
||||
onChange={this.handleAuthorChange}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Say something..."
|
||||
value={this.state.text}
|
||||
onChange={this.handleTextChange}
|
||||
/>
|
||||
<form className="commentForm" onSubmit={this.handleSubmit}>
|
||||
<input type="text" placeholder="Your name" ref="author" />
|
||||
<input type="text" placeholder="Say something..." ref="text" />
|
||||
<input type="submit" value="Post" />
|
||||
</form>
|
||||
);
|
||||
@@ -522,60 +521,14 @@ var CommentForm = React.createClass({
|
||||
|
||||
##### 事件
|
||||
|
||||
React使用小驼峰命名规范(camelCase)给组件绑定事件处理器。我们附加 `onChange` 给两个 `<input>` 元素。现在,当用户输入文本到 `<input>` 中,被附加的 `onChange` 回调函数被激发并且组件的 `state` 被修改。然后,被渲染的 `input` 元素的值将会更新以反映当前组件的 `state`。
|
||||
|
||||
#### 提交表单
|
||||
|
||||
让我们使表单具有交互性。当用户提交表单时,我们应该清除它,提交一个请求到服务器,并刷新评论列表。让我们监听表单的提交事件并清除它。
|
||||
|
||||
```javascript{12-21,24}
|
||||
// tutorial17.js
|
||||
var CommentForm = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {author: '', text: ''};
|
||||
},
|
||||
handleAuthorChange: function(e) {
|
||||
this.setState({author: e.target.value});
|
||||
},
|
||||
handleTextChange: function(e) {
|
||||
this.setState({text: e.target.value});
|
||||
},
|
||||
handleSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
var author = this.state.author.trim();
|
||||
var text = this.state.text.trim();
|
||||
if (!text || !author) {
|
||||
return;
|
||||
}
|
||||
// TODO: send request to the server
|
||||
this.setState({author: '', text: ''});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<form className="commentForm" onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Your name"
|
||||
value={this.state.author}
|
||||
onChange={this.handleAuthorChange}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Say something..."
|
||||
value={this.state.text}
|
||||
onChange={this.handleTextChange}
|
||||
/>
|
||||
<input type="submit" value="Post" />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
我们给表单绑定一个`onSubmit`处理器,它在表单提交了合法的输入后清空表单字段。
|
||||
React使用驼峰命名规范(camelCase)给组件绑定事件处理器。我们给表单绑定一个`onSubmit`处理器,它在表单提交了合法的输入后清空表单字段。
|
||||
|
||||
在事件中调用`preventDefault()`来阻止浏览器提交表单的默认行为。
|
||||
|
||||
##### Refs
|
||||
|
||||
我们利用`ref`属性给子组件赋予名字,`this.refs`-引用组件。我们可以在组件上调用 `React.findDOMNode(component)` 获取原生的浏览器DOM元素。
|
||||
|
||||
##### 回调函数作为属性
|
||||
|
||||
当用户提交评论时,我们需要刷新评论列表来包含这条新评论。在`CommentBox`中完成所有逻辑是有道理的,因为`CommentBox` 拥有代表了评论列表的状态(state)。
|
||||
@@ -583,7 +536,7 @@ var CommentForm = React.createClass({
|
||||
我们需要从子组件传回数据到它的父组件。我们在父组件的`render`方法中以传递一个新的回调函数(`handleCommentSubmit`)到子组件完成这件事,绑定它到子组件的 `onCommentSubmit` 事件上。无论事件什么时候触发,回调函数都将被调用:
|
||||
|
||||
```javascript{16-18,31}
|
||||
// tutorial18.js
|
||||
// tutorial17.js
|
||||
var CommentBox = React.createClass({
|
||||
loadCommentsFromServer: function() {
|
||||
$.ajax({
|
||||
@@ -620,45 +573,28 @@ var CommentBox = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
既然 `CommentBox` 已经通过 `onCommentSubmit` prop 使回调函数对于 `CommentForm` 可用,`CommentForm` 就可以在用户提交表单时调用回调函数:
|
||||
当用户提交表单时,让我们从 `CommentForm` 调用这个回调函数:
|
||||
|
||||
```javascript{19}
|
||||
// tutorial19.js
|
||||
```javascript{10}
|
||||
// tutorial18.js
|
||||
var CommentForm = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {author: '', text: ''};
|
||||
},
|
||||
handleAuthorChange: function(e) {
|
||||
this.setState({author: e.target.value});
|
||||
},
|
||||
handleTextChange: function(e) {
|
||||
this.setState({text: e.target.value});
|
||||
},
|
||||
handleSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
var author = this.state.author.trim();
|
||||
var text = this.state.text.trim();
|
||||
var author = React.findDOMNode(this.refs.author).value.trim();
|
||||
var text = React.findDOMNode(this.refs.text).value.trim();
|
||||
if (!text || !author) {
|
||||
return;
|
||||
}
|
||||
this.props.onCommentSubmit({author: author, text: text});
|
||||
this.setState({author: '', text: ''});
|
||||
React.findDOMNode(this.refs.author).value = '';
|
||||
React.findDOMNode(this.refs.text).value = '';
|
||||
return;
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<form className="commentForm" onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Your name"
|
||||
value={this.state.author}
|
||||
onChange={this.handleAuthorChange}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Say something..."
|
||||
value={this.state.text}
|
||||
onChange={this.handleTextChange}
|
||||
/>
|
||||
<input type="text" placeholder="Your name" ref="author" />
|
||||
<input type="text" placeholder="Say something..." ref="text" />
|
||||
<input type="submit" value="Post" />
|
||||
</form>
|
||||
);
|
||||
@@ -669,7 +605,7 @@ var CommentForm = React.createClass({
|
||||
既然现在回调函数已经就绪,我们所需要做的就是提交到服务器然后刷新列表:
|
||||
|
||||
```javascript{17-28}
|
||||
// tutorial20.js
|
||||
// tutorial19.js
|
||||
var CommentBox = React.createClass({
|
||||
loadCommentsFromServer: function() {
|
||||
$.ajax({
|
||||
@@ -721,8 +657,8 @@ var CommentBox = React.createClass({
|
||||
|
||||
我们的应用现在已经功能完备,但是它感觉很慢,因为在评论出现在列表前必须等待请求完成。我们可以优化添加这条评论到列表以使应用感觉更快。
|
||||
|
||||
```javascript{17-23,33}
|
||||
// tutorial21.js
|
||||
```javascript{17-19,29}
|
||||
// tutorial20.js
|
||||
var CommentBox = React.createClass({
|
||||
loadCommentsFromServer: function() {
|
||||
$.ajax({
|
||||
@@ -739,10 +675,6 @@ var CommentBox = React.createClass({
|
||||
},
|
||||
handleCommentSubmit: function(comment) {
|
||||
var comments = this.state.data;
|
||||
// Optimistically set an id on the new comment. It will be replaced by an
|
||||
// id generated by the server. In a production application you would likely
|
||||
// not use Date.now() for this and would have a more robust system in place.
|
||||
comment.id = Date.now();
|
||||
var newComments = comments.concat([comment]);
|
||||
this.setState({data: newComments});
|
||||
$.ajax({
|
||||
|
||||
@@ -29,7 +29,7 @@ Un video di [tagtree.tv](http://tagtree.tv/) che espone i principi di [Pensare i
|
||||
|
||||
* * *
|
||||
|
||||
### Pensare in grande con React ###
|
||||
### Pensare in grande con React
|
||||
|
||||
"Sulla carta, tutti questi framework JS sembrano promettenti: implementazioni pulite, design veloce del codice, esecuzione perfetta. Ma che succede quando metti Javascript sotto stress? Che succede se gli dài in pasto 6 megabyte di codice? In questo talk investigheremo come si comporta React in situazioni di stress elevato, e come ha aiutato il nostro team a costruire codice sicuro ad una scala enorme."
|
||||
<figure>[](https://skillsmatter.com/skillscasts/5429-going-big-with-react#video)</figure>
|
||||
|
||||
@@ -29,7 +29,7 @@ A [tagtree.tv](http://tagtree.tv/) video conveying the principles of [Thinking i
|
||||
|
||||
* * *
|
||||
|
||||
### Going big with React ###
|
||||
### Going big with React
|
||||
|
||||
"On paper, all those JS frameworks look promising: clean implementations, quick code design, flawless execution. But what happens when you stress test Javascript? What happens when you throw 6 megabytes of code at it? In this talk, we'll investigate how React performs in a high stress situation, and how it has helped our team build safe code on a massive scale."
|
||||
<figure>[](https://skillsmatter.com/skillscasts/5429-going-big-with-react#video)</figure>
|
||||
|
||||
@@ -29,7 +29,7 @@ next: complementary-tools-zh-CN.html
|
||||
|
||||
* * *
|
||||
|
||||
### Going big with React ###
|
||||
### Going big with React
|
||||
|
||||
"理论上,所有的JS框架都大有可为:干净的实现,快速的代码设计,完美的执行。但是当你压力测试时Javascript会怎样?当你丢进6MB的代码时会怎样?在这次演讲中,我们会探究React在高压环境下如何表现,以及它如何帮助我们的团队在大规模时构建安全代码。 "
|
||||
<figure>[](https://skillsmatter.com/skillscasts/5429-going-big-with-react#video)</figure>
|
||||
|
||||
@@ -24,32 +24,48 @@ If you're just starting out, make sure to use the development version.
|
||||
The uncompressed, development version of [react.js](https://fb.me/react-{{site.react_version}}.js) and [react-dom.js](https://fb.me/react-dom-{{site.react_version}}.js) with inline documentation (you need both files).
|
||||
|
||||
```html
|
||||
<script src="https://fb.me/react-{{site.react_version}}.js"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.js"></script>
|
||||
<script src="https://fb.me/react-{{site.react_version}}.js"
|
||||
integrity="sha384-{{site.react_hashes.dev}}"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.js"
|
||||
integrity="sha384-{{site.react_hashes.dom_dev}}"
|
||||
crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
#### React {{site.react_version}} (production)
|
||||
The compressed, production version of [react.js](https://fb.me/react-{{site.react_version}}.min.js) and [react-dom.js](https://fb.me/react-dom-{{site.react_version}}.min.js) (you need both).
|
||||
|
||||
```html
|
||||
<script src="https://fb.me/react-{{site.react_version}}.min.js"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.min.js"></script>
|
||||
<script src="https://fb.me/react-{{site.react_version}}.min.js"
|
||||
integrity="sha384-{{site.react_hashes.prod}}"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.min.js"
|
||||
integrity="sha384-{{site.react_hashes.dom_prod}}"
|
||||
crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
#### React with Add-Ons {{site.react_version}} (development)
|
||||
The uncompressed, development version of React with [optional add-ons](/react/docs/addons.html).
|
||||
|
||||
```html
|
||||
<script src="https://fb.me/react-with-addons-{{site.react_version}}.js"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.js"></script>
|
||||
<script src="https://fb.me/react-with-addons-{{site.react_version}}.js"
|
||||
integrity="sha384-{{site.react_hashes.addons_dev}}"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.js"
|
||||
integrity="sha384-{{site.react_hashes.dom_dev}}"
|
||||
crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
#### React with Add-Ons {{site.react_version}} (production)
|
||||
The compressed, production version of React with [optional add-ons](/react/docs/addons.html).
|
||||
|
||||
```html
|
||||
<script src="https://fb.me/react-with-addons-{{site.react_version}}.min.js"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.min.js"></script>
|
||||
<script src="https://fb.me/react-with-addons-{{site.react_version}}.min.js"
|
||||
integrity="sha384-{{site.react_hashes.addons_prod}}"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://fb.me/react-dom-{{site.react_version}}.min.js"
|
||||
integrity="sha384-{{site.react_hashes.dom_prod}}"
|
||||
crossorigin="anonymous"></script>
|
||||
```
|
||||
|
||||
All scripts are also available via [CDNJS](https://cdnjs.com/libraries/react/).
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2015, Facebook, Inc.
|
||||
* Copyright 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2015, Facebook, Inc.
|
||||
* Copyright 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
module.exports = function(context) {
|
||||
// we also allow literal strings and concatinated literal strings
|
||||
// we also allow literal strings and concatenated literal strings
|
||||
function getLiteralString(node) {
|
||||
if (node.type === 'Literal' && typeof node.value === 'string') {
|
||||
return node.value;
|
||||
@@ -67,7 +67,7 @@ module.exports = function(context) {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// count the number of formating substitutions, plus the first two args
|
||||
// count the number of formatting substitutions, plus the first two args
|
||||
var expectedNArgs = (format.match(/%s/g) || []).length + 2;
|
||||
if (node.arguments.length !== expectedNArgs) {
|
||||
context.report(
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
<p>
|
||||
If you can see this, React is not running. Try running:
|
||||
</p>
|
||||
<pre>npm install -g babel
|
||||
<pre>npm install -g babel-cli
|
||||
cd examples/basic-jsx-precompile/
|
||||
babel example.js --out-dir=build</pre>
|
||||
npm install babel-preset-react
|
||||
babel example.js --presets react --out-dir=build</pre>
|
||||
</div>
|
||||
<h4>Example Details</h4>
|
||||
<p>This is written with JSX in a separate file and precompiled to vanilla JS by running:</p>
|
||||
@@ -25,8 +26,8 @@ babel example.js --out-dir=build</pre>
|
||||
|
||||
<p><i>With Babel 6.0 or higher</i></p>
|
||||
<pre>npm install -g babel-cli
|
||||
npm install babel-preset-react
|
||||
cd examples/basic-jsx-precompile/
|
||||
npm install babel-preset-react
|
||||
babel example.js --presets react --out-dir=build</pre>
|
||||
<p>
|
||||
Learn more about React at
|
||||
|
||||
15
examples/jquery-bootstrap/js/app.js
vendored
15
examples/jquery-bootstrap/js/app.js
vendored
@@ -19,9 +19,13 @@ var BootstrapModal = React.createClass({
|
||||
componentDidMount: function() {
|
||||
// When the component is added, turn it into a modal
|
||||
$(this.refs.root).modal({backdrop: 'static', keyboard: false, show: false});
|
||||
|
||||
// Bootstrap's modal class exposes a few events for hooking into modal
|
||||
// functionality. Lets hook into one of them:
|
||||
$(this.refs.root).on('hidden.bs.modal', this.handleHidden);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
$(this.refs.root).off('hidden', this.handleHidden);
|
||||
$(this.refs.root).off('hidden.bs.modal', this.handleHidden);
|
||||
},
|
||||
close: function() {
|
||||
$(this.refs.root).modal('hide');
|
||||
@@ -84,6 +88,11 @@ var BootstrapModal = React.createClass({
|
||||
if (this.props.onConfirm) {
|
||||
this.props.onConfirm();
|
||||
}
|
||||
},
|
||||
handleHidden: function() {
|
||||
if (this.props.onHidden) {
|
||||
this.props.onHidden();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -102,6 +111,7 @@ var Example = React.createClass({
|
||||
cancel="Cancel"
|
||||
onCancel={this.handleCancel}
|
||||
onConfirm={this.closeModal}
|
||||
onHidden={this.handleModalDidClose}
|
||||
title="Hello, Bootstrap!">
|
||||
This is a React component powered by jQuery and Bootstrap!
|
||||
</BootstrapModal>
|
||||
@@ -120,6 +130,9 @@ var Example = React.createClass({
|
||||
},
|
||||
closeModal: function() {
|
||||
this.refs.modal.close();
|
||||
},
|
||||
handleModalDidClose: function() {
|
||||
alert("The modal has been dismissed!");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* <%= package %> v<%= version %>
|
||||
*
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
|
||||
@@ -16,18 +16,15 @@ module.exports = function() {
|
||||
config.globalTransforms = config.globalTransforms || [];
|
||||
config.plugins = config.plugins || [];
|
||||
config.after = config.after || [];
|
||||
config.paths = config.paths || [];
|
||||
|
||||
// create the bundle we'll work with
|
||||
var entries = grunt.file.expand(config.entries);
|
||||
var paths = grunt.file.expand(config.paths);
|
||||
|
||||
// Extract other options
|
||||
var options = {
|
||||
entries: entries,
|
||||
debug: config.debug, // sourcemaps
|
||||
standalone: config.standalone, // global
|
||||
paths: paths,
|
||||
};
|
||||
|
||||
var bundle = browserify(options);
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
var grunt = require('grunt');
|
||||
|
||||
var extension = process.platform === 'win32' ? '.cmd': '';
|
||||
|
||||
module.exports = function() {
|
||||
var done = this.async();
|
||||
grunt.util.spawn({
|
||||
cmd: 'node_modules/.bin/eslint',
|
||||
cmd: 'node_modules/.bin/eslint' + extension,
|
||||
args: ['.'],
|
||||
opts: {stdio: 'inherit'}, // allows colors to passthrough
|
||||
}, function(err, result, code) {
|
||||
|
||||
@@ -6,20 +6,88 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var async = require('async');
|
||||
var fs = require('fs');
|
||||
var glob = require('glob');
|
||||
var grunt = require('grunt');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function() {
|
||||
var done = this.async();
|
||||
var rootPath = path.resolve('.');
|
||||
var buildPath = path.join(rootPath, 'build');
|
||||
var tempConfigPath = path.join(buildPath, 'jest-config.json');
|
||||
|
||||
var config = require(path.join(rootPath, 'package.json')).jest;
|
||||
|
||||
var collectCoverageOnlyFrom = {
|
||||
'src/**/*.js': {
|
||||
ignore: [
|
||||
'src/**/__tests__/*.js',
|
||||
'src/shared/vendor/third_party/*.js',
|
||||
'src/test/*.js',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
function getCollectCoverageOnlyFrom(callback) {
|
||||
var patterns = Object.keys(collectCoverageOnlyFrom);
|
||||
var result = {};
|
||||
|
||||
async.each(patterns, function(pattern) {
|
||||
var options = Object.assign({ nodir: true }, collectCoverageOnlyFrom[pattern]);
|
||||
glob(pattern, options, function(err, files) {
|
||||
(files || []).reduce(function(object, key) {
|
||||
object[key] = true;
|
||||
return object;
|
||||
}, result);
|
||||
|
||||
callback(err);
|
||||
});
|
||||
}, function(err) {
|
||||
callback(err, result);
|
||||
});
|
||||
}
|
||||
|
||||
function getJestConfig(callback) {
|
||||
var rootDir = path.resolve(buildPath, path.resolve(config.rootDir));
|
||||
getCollectCoverageOnlyFrom(function(err, data) {
|
||||
callback(err, Object.assign({}, config, {
|
||||
rootDir: rootDir,
|
||||
collectCoverage: true,
|
||||
collectCoverageOnlyFrom: data,
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
grunt.log.error('jest failed');
|
||||
grunt.log.error(err);
|
||||
}
|
||||
|
||||
function writeTempConfig(callback) {
|
||||
getJestConfig(function(err, data) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
grunt.file.mkdir(buildPath);
|
||||
fs.writeFile(tempConfigPath, JSON.stringify(data, null, ' '), 'utf8', callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function run(done, configPath) {
|
||||
grunt.log.writeln('running jest (this may take a while)');
|
||||
|
||||
var args = ['--harmony', path.join('node_modules', 'jest-cli', 'bin', 'jest')];
|
||||
if (configPath) {
|
||||
args.push('--config', configPath);
|
||||
}
|
||||
grunt.util.spawn({
|
||||
cmd: 'node',
|
||||
args: ['--harmony', path.join('node_modules', 'jest-cli', 'bin', 'jest')],
|
||||
opts: {stdio: 'inherit', env: {NODE_ENV: 'test'}},
|
||||
}, function(err, result, code) {
|
||||
if (err) {
|
||||
grunt.log.error('jest failed');
|
||||
grunt.log.error(err);
|
||||
args: args,
|
||||
opts: { stdio: 'inherit', env: { NODE_ENV: 'test' } },
|
||||
}, function(spawnErr, result, code) {
|
||||
if (spawnErr) {
|
||||
onError(spawnErr);
|
||||
} else {
|
||||
grunt.log.ok('jest passed');
|
||||
}
|
||||
@@ -27,4 +95,27 @@ module.exports = function() {
|
||||
|
||||
done(code === 0);
|
||||
});
|
||||
}
|
||||
|
||||
function runJestNormally() {
|
||||
var done = this.async();
|
||||
run(done);
|
||||
}
|
||||
|
||||
function runJestWithCoverage() {
|
||||
var done = this.async();
|
||||
|
||||
writeTempConfig(function(writeErr) {
|
||||
if (writeErr) {
|
||||
onError(writeErr);
|
||||
return;
|
||||
}
|
||||
|
||||
run(done, tempConfigPath);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
normal: runJestNormally,
|
||||
coverage: runJestWithCoverage,
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = function() {
|
||||
});
|
||||
|
||||
args.push.apply(args, rootIDs);
|
||||
args.push('--config' /* from stdin */);
|
||||
args.push('--config');
|
||||
|
||||
var child = spawn({
|
||||
cmd: 'node',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var assign = require('object-assign');
|
||||
var fs = require('fs');
|
||||
var grunt = require('grunt');
|
||||
var path = require('path');
|
||||
@@ -36,11 +35,6 @@ var addons = {
|
||||
name: 'transition-group',
|
||||
docs: 'animation',
|
||||
},
|
||||
cloneWithProps: {
|
||||
module: 'cloneWithProps',
|
||||
name: 'clone-with-props',
|
||||
docs: 'clone-with-props',
|
||||
},
|
||||
createFragment: {
|
||||
module: 'ReactFragment',
|
||||
method: 'create',
|
||||
@@ -81,7 +75,7 @@ function buildReleases() {
|
||||
var destLicense = path.join(destDir, 'LICENSE');
|
||||
var destPatents = path.join(destDir, 'PATENTS');
|
||||
|
||||
var pkgData = assign({}, pkgTemplate);
|
||||
var pkgData = Object.assign({}, pkgTemplate);
|
||||
pkgData.name = pkgName;
|
||||
|
||||
grunt.file.mkdir(destDir);
|
||||
|
||||
@@ -14,8 +14,6 @@ var BOWER_FILES = [
|
||||
'react-dom-server.js',
|
||||
'react-dom-server.min.js',
|
||||
];
|
||||
var GH_PAGES_PATH = '../react-gh-pages/';
|
||||
var GH_PAGES_GLOB = [GH_PAGES_PATH + '*'];
|
||||
|
||||
var EXAMPLES_PATH = 'examples/';
|
||||
var EXAMPLES_GLOB = [EXAMPLES_PATH + '**/*.*'];
|
||||
@@ -77,12 +75,6 @@ function setup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!grunt.file.exists(GH_PAGES_PATH)) {
|
||||
grunt.log.error('Make sure you have the react gh-pages branch checked ' +
|
||||
'out at ../react-gh-pages.');
|
||||
return false;
|
||||
}
|
||||
|
||||
VERSION = grunt.config.data.pkg.version;
|
||||
VERSION_STRING = 'v' + VERSION;
|
||||
}
|
||||
@@ -106,28 +98,9 @@ function bower() {
|
||||
}
|
||||
|
||||
function docs() {
|
||||
var done = this.async();
|
||||
|
||||
grunt.file.copy('build/react-' + VERSION + '.zip', 'docs/downloads/react-' + VERSION + '.zip');
|
||||
grunt.file.copy('build/react.js', 'docs/js/react.js');
|
||||
grunt.file.copy('build/react-dom.js', 'docs/js/react-dom.js');
|
||||
|
||||
var files = grunt.file.expand(GH_PAGES_GLOB);
|
||||
files.forEach(function(file) {
|
||||
grunt.file.delete(file, {force: true});
|
||||
});
|
||||
|
||||
// Build the docs with `rake release`, which will compile the CSS & JS, then
|
||||
// build jekyll into GH_PAGES_PATH
|
||||
var rakeOpts = {
|
||||
cmd: 'rake',
|
||||
args: ['release'],
|
||||
opts: {cwd: 'docs'},
|
||||
};
|
||||
grunt.util.spawn(rakeOpts, function() {
|
||||
// Commit the repo. We don't really care about tagging this.
|
||||
_gitCommitAndTag(GH_PAGES_PATH, VERSION_STRING, null, done);
|
||||
});
|
||||
}
|
||||
|
||||
function msg() {
|
||||
@@ -136,8 +109,10 @@ function msg() {
|
||||
var steps = [
|
||||
'Still todo:',
|
||||
'* put files on CDN',
|
||||
'* add starter pack (git add -f docs/downloads/react-version.zip)',
|
||||
'* push changes to git repositories',
|
||||
'* publish npm module (`npm publish .`)',
|
||||
'* update docs branch variable in Travis CI',
|
||||
'* publish npm modules',
|
||||
'* announce it on FB/Twitter/mailing list',
|
||||
];
|
||||
steps.forEach(function(ln) {
|
||||
|
||||
21
gulpfile.js
21
gulpfile.js
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
@@ -14,8 +14,7 @@ var babel = require('gulp-babel');
|
||||
var flatten = require('gulp-flatten');
|
||||
var del = require('del');
|
||||
|
||||
var babelPluginDEV = require('fbjs-scripts/babel/dev-expression');
|
||||
var babelPluginModules = require('fbjs-scripts/babel/rewrite-modules');
|
||||
var babelPluginModules = require('fbjs-scripts/babel-6/rewrite-modules');
|
||||
|
||||
var paths = {
|
||||
react: {
|
||||
@@ -23,26 +22,20 @@ var paths = {
|
||||
'src/**/*.js',
|
||||
'!src/**/__tests__/**/*.js',
|
||||
'!src/**/__mocks__/**/*.js',
|
||||
'!src/shared/vendor/**/*.js',
|
||||
],
|
||||
lib: 'build/modules',
|
||||
},
|
||||
};
|
||||
|
||||
var babelOpts = {
|
||||
nonStandard: true,
|
||||
blacklist: [
|
||||
'spec.functionName',
|
||||
plugins: [
|
||||
[babelPluginModules, { map: require('fbjs/module-map') }],
|
||||
],
|
||||
optional: [
|
||||
'es7.trailingFunctionCommas',
|
||||
],
|
||||
plugins: [babelPluginDEV, babelPluginModules],
|
||||
ignore: ['third_party'],
|
||||
_moduleMap: require('fbjs/module-map'),
|
||||
};
|
||||
|
||||
gulp.task('react:clean', function(cb) {
|
||||
del([paths.react.lib], cb);
|
||||
gulp.task('react:clean', function() {
|
||||
return del([paths.react.lib]);
|
||||
});
|
||||
|
||||
gulp.task('react:modules', function() {
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ClientReactRootIndex
|
||||
* @typechecks
|
||||
* @providesModule ReactElementTestChild
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var nextReactRootIndex = 0;
|
||||
var React = require('React');
|
||||
|
||||
var ClientReactRootIndex = {
|
||||
createReactRootIndex: function() {
|
||||
return nextReactRootIndex++;
|
||||
var Child = React.createClass({
|
||||
render: function() {
|
||||
return React.createElement('div');
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
module.exports = ClientReactRootIndex;
|
||||
module.exports = Child;
|
||||
35
mocks/ReactMockedComponentTestComponent.js
Normal file
35
mocks/ReactMockedComponentTestComponent.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ReactMockedComponentTestComponent
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var ReactMockedComponentTestComponent = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {bar: 'baz'};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {foo: 'bar'};
|
||||
},
|
||||
|
||||
hasCustomMethod: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <span />;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
module.exports = ReactMockedComponentTestComponent;
|
||||
9278
npm-shrinkwrap.json
generated
9278
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
68
package.json
68
package.json
@@ -1,47 +1,65 @@
|
||||
{
|
||||
"name": "react-build",
|
||||
"private": true,
|
||||
"version": "0.14.8",
|
||||
"version": "15.0.0-rc.1",
|
||||
"devDependencies": {
|
||||
"babel": "^5.8.3",
|
||||
"babel-eslint": "^4.1.3",
|
||||
"benchmark": "^1.0.0",
|
||||
"browserify": "^9.0.3",
|
||||
"async": "^1.5.0",
|
||||
"babel-cli": "^6.6.5",
|
||||
"babel-core": "^6.0.0",
|
||||
"babel-eslint": "^5.0.0",
|
||||
"babel-plugin-check-es2015-constants": "^6.5.0",
|
||||
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-arrow-functions": "^6.5.2",
|
||||
"babel-plugin-transform-es2015-block-scoped-functions": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-block-scoping": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-classes": "^6.5.2",
|
||||
"babel-plugin-transform-es2015-computed-properties": "^6.5.2",
|
||||
"babel-plugin-transform-es2015-destructuring": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-for-of": "^6.5.2",
|
||||
"babel-plugin-transform-es2015-literals": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.5.2",
|
||||
"babel-plugin-transform-es2015-object-super": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-parameters": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-shorthand-properties": "^6.5.0",
|
||||
"babel-plugin-transform-es2015-spread": "^6.5.2",
|
||||
"babel-plugin-transform-es2015-template-literals": "^6.5.2",
|
||||
"babel-plugin-transform-es3-member-expression-literals": "^6.5.0",
|
||||
"babel-plugin-transform-es3-property-literals": "^6.5.0",
|
||||
"babel-preset-react": "^6.5.0",
|
||||
"browserify": "^12.0.1",
|
||||
"bundle-collapser": "^1.1.1",
|
||||
"coffee-script": "^1.8.0",
|
||||
"del": "^1.2.0",
|
||||
"derequire": "^2.0.0",
|
||||
"coveralls": "^2.11.6",
|
||||
"del": "^2.0.2",
|
||||
"derequire": "^2.0.3",
|
||||
"envify": "^3.0.0",
|
||||
"eslint": "^1.5.1",
|
||||
"eslint-plugin-react": "^3.4.2",
|
||||
"eslint": "1.10.3",
|
||||
"eslint-plugin-react": "4.1.0",
|
||||
"eslint-plugin-react-internal": "file:eslint-rules",
|
||||
"fbjs": "^0.6.1",
|
||||
"fbjs-scripts": "^0.2.0",
|
||||
"fbjs": "^0.8.0-alpha.2",
|
||||
"fbjs-scripts": "^0.6.0-alpha.3",
|
||||
"glob": "^6.0.1",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-cli": "^0.1.13",
|
||||
"grunt-compare-size": "^0.4.0",
|
||||
"grunt-contrib-clean": "^0.6.0",
|
||||
"grunt-contrib-compress": "^0.13.0",
|
||||
"grunt-contrib-compress": "^0.14.0",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-babel": "^5.1.0",
|
||||
"gulp-flatten": "^0.1.0",
|
||||
"gulp-util": "^3.0.5",
|
||||
"gulp-babel": "^6.0.0",
|
||||
"gulp-flatten": "^0.2.0",
|
||||
"gzip-js": "~0.3.2",
|
||||
"jest-cli": "^0.5.7",
|
||||
"jstransform": "^11.0.0",
|
||||
"object-assign": "^3.0.0",
|
||||
"optimist": "^0.6.1",
|
||||
"jest-cli": "^0.9.0",
|
||||
"platform": "^1.1.0",
|
||||
"run-sequence": "^1.1.0",
|
||||
"run-sequence": "^1.1.4",
|
||||
"through2": "^2.0.0",
|
||||
"tmp": "~0.0.18",
|
||||
"tmp": "~0.0.28",
|
||||
"typescript": "~1.4.0",
|
||||
"uglify-js": "^2.4.23",
|
||||
"uglify-js": "^2.5.0",
|
||||
"uglifyify": "^3.0.1"
|
||||
},
|
||||
"devEngines": {
|
||||
"node": "4.x",
|
||||
"npm": "2.x"
|
||||
"node": "4.x || 5.x",
|
||||
"npm": "2.x || 3.x"
|
||||
},
|
||||
"commonerConfig": {
|
||||
"version": 7
|
||||
@@ -63,6 +81,7 @@
|
||||
"scriptPreprocessor": "scripts/jest/preprocessor.js",
|
||||
"setupEnvScriptFile": "scripts/jest/environment.js",
|
||||
"setupTestFrameworkScriptFile": "scripts/jest/test-framework-setup.js",
|
||||
"testRunner": "jasmine1",
|
||||
"testFileExtensions": [
|
||||
"coffee",
|
||||
"js",
|
||||
@@ -70,6 +89,7 @@
|
||||
],
|
||||
"testPathDirs": [
|
||||
"<rootDir>/eslint-rules",
|
||||
"<rootDir>/mocks",
|
||||
"<rootDir>/src",
|
||||
"node_modules/fbjs"
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* Copyright 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-addons-template",
|
||||
"version": "0.14.8",
|
||||
"version": "15.0.0-rc.1",
|
||||
"main": "index.js",
|
||||
"repository": "facebook/react",
|
||||
"keywords": [
|
||||
@@ -10,6 +10,6 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.8"
|
||||
"react": "^15.0.0-rc.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
## react-codemod
|
||||
|
||||
This repository contains a collection of codemod scripts based for use with
|
||||
[JSCodeshift](https://github.com/facebook/jscodeshift) that help update React
|
||||
APIs.
|
||||
|
||||
### Setup & Run
|
||||
|
||||
* `npm install -g jscodeshift`
|
||||
* `git clone https://github.com/facebook/react.git` or download a zip file
|
||||
from `https://github.com/facebook/react/archive/master.zip`
|
||||
* `jscodeshift -t <codemod-script> <file>`
|
||||
* Use the `-d` option for a dry-run and use `-p` to print the output
|
||||
for comparison
|
||||
|
||||
### Included Scripts
|
||||
|
||||
`findDOMNode` updates `this.getDOMNode()` or `this.refs.foo.getDOMNode()`
|
||||
calls inside of `React.createClass` components to `React.findDOMNode(foo)`. Note
|
||||
that it will only look at code inside of `React.createClass` calls and only
|
||||
update calls on the component instance or its refs. You can use this script to
|
||||
update most calls to `getDOMNode` and then manually go through the remaining
|
||||
calls.
|
||||
|
||||
* `jscodeshift -t react/packages/react-codemod/transforms/findDOMNode.js <file>`
|
||||
|
||||
`react-to-react-dom` updates code for the split of the `react` and `react-dom`
|
||||
packages (e.g., `React.render` to `ReactDOM.render`). It looks for
|
||||
`require('react')` and replaces the appropriate property accesses using
|
||||
`require('react-dom')`. It does not support ES6 modules or other non-CommonJS
|
||||
systems. We recommend performing the `findDOMNode` conversion first.
|
||||
|
||||
* `jscodeshift -t react/packages/react-codemod/transforms/react-to-react-dom.js <file>`
|
||||
* After running the automated codemod, you may want to run a regex-based find-and-replace to remove extra whitespace between the added requires, such as `codemod.py -m -d src --extensions js '(var React\s*=\s*require\(.react.\);)\n\n(\s*var ReactDOM)' '\1\n\2'` using https://github.com/facebook/codemod.
|
||||
|
||||
`pure-render-mixin` removes `PureRenderMixin` and inlines
|
||||
`shouldComponentUpdate` so that the ES2015 class transform can pick up the React
|
||||
component and turn it into an ES2015 class. NOTE: This currently only works if you
|
||||
are using the master version (>0.13.1) of React as it is using
|
||||
`React.addons.shallowCompare`
|
||||
|
||||
* `jscodeshift -t react/packages/react-codemod/transforms/pure-render-mixin.js <file>`
|
||||
* If `--mixin-name=<name>` is specified it will look for the specified name
|
||||
instead of `PureRenderMixin`. Note that it is not possible to use a
|
||||
namespaced name for the mixin. `mixins: [React.addons.PureRenderMixin]` will
|
||||
not currently work.
|
||||
|
||||
`class` transforms `React.createClass` calls into ES2015 classes.
|
||||
|
||||
* `jscodeshift -t react/packages/react-codemod/transforms/class.js <file>`
|
||||
* If `--no-super-class` is specified it will not extend
|
||||
`React.Component` if `setState` and `forceUpdate` aren't being called in a
|
||||
class. We do recommend always extending from `React.Component`, especially
|
||||
if you are using or planning to use [Flow](http://flowtype.org/). Also make
|
||||
sure you are not calling `setState` anywhere outside of your component.
|
||||
|
||||
These three scripts take an option `--no-explicit-require` if you don't have a
|
||||
`require('React')` statement in your code files and if you access React as a
|
||||
global.
|
||||
|
||||
### Explanation of the ES2015 class transform
|
||||
|
||||
* Ignore components with calls to deprecated APIs. This is very defensive, if
|
||||
the script finds any identifiers called `isMounted`, `getDOMNode`,
|
||||
`replaceProps`, `replaceState` or `setProps` it will skip the component.
|
||||
* Replaces `var A = React.createClass(spec)` with
|
||||
`class A (extends React.Component) {spec}`.
|
||||
* Pulls out all statics defined on `statics` plus the few special cased
|
||||
statics like `propTypes`, `childContextTypes`, `contextTypes` and
|
||||
`displayName` and assigns them after the class is created.
|
||||
`class A {}; A.foo = bar;`
|
||||
* Takes `getDefaultProps` and inlines it as a static `defaultProps`.
|
||||
If `getDefaultProps` is defined as a function with a single statement that
|
||||
returns an object, it optimizes and transforms
|
||||
`getDefaultProps() { return {foo: 'bar'}; }` into
|
||||
`A.defaultProps = {foo: 'bar'};`. If `getDefaultProps` contains more than
|
||||
one statement it will transform into a self-invoking function like this:
|
||||
`A.defaultProps = function() {…}();`. Note that this means that the function
|
||||
will be executed only a single time per app-lifetime. In practice this
|
||||
hasn't caused any issues – `getDefaultProps` should not contain any
|
||||
side-effects.
|
||||
* Binds class methods to the instance if methods are referenced without being
|
||||
called directly. It checks for `this.foo` but also traces variable
|
||||
assignments like `var self = this; self.foo`. It does not bind functions
|
||||
from the React API and ignores functions that are being called directly
|
||||
(unless it is both called directly and passed around to somewhere else)
|
||||
* Creates a constructor if necessary. This is necessary if either
|
||||
`getInitialState` exists in the `React.createClass` spec OR if functions
|
||||
need to be bound to the instance.
|
||||
* When `--no-super-class` is passed it only optionally extends
|
||||
`React.Component` when `setState` or `forceUpdate` are used within the
|
||||
class.
|
||||
|
||||
The constructor logic is as follows:
|
||||
* Call `super(props, context)` if the base class needs to be extended.
|
||||
* Bind all functions that are passed around,
|
||||
like `this.foo = this.foo.bind(this)`
|
||||
* Inline `getInitialState` (and remove `getInitialState` from the spec). It
|
||||
also updates access of `this.props.foo` to `props.foo` and adds `props` as
|
||||
argument to the constructor. This is necessary in the case when the base
|
||||
class does not need to be extended where `this.props` will only be set by
|
||||
React after the constructor has been run.
|
||||
* Changes `return StateObject` from `getInitialState` to assign `this.state`
|
||||
directly.
|
||||
|
||||
### Recast Options
|
||||
|
||||
Options to [recast](https://github.com/benjamn/recast)'s printer can be provided
|
||||
through the `printOptions` command line argument
|
||||
|
||||
* `jscodeshift -t transform.js <file> --printOptions='{"quote":"double"}'`
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "react-codemod",
|
||||
"version": "3.0.0",
|
||||
"description": "React codemod scripts",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"jscodeshift": "^0.3.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-jest": "^5.3.0",
|
||||
"jest-cli": "^0.5.1"
|
||||
},
|
||||
"jest": {
|
||||
"scriptPreprocessor": "./node_modules/babel-jest",
|
||||
"testPathDirs": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
var fs = require('fs');
|
||||
var jscodeshift = require('jscodeshift');
|
||||
|
||||
function read(fileName) {
|
||||
return fs.readFileSync(__dirname + '/../' + fileName, 'utf8');
|
||||
}
|
||||
|
||||
function test(transformName, testFileName, options) {
|
||||
var path = testFileName + '.js';
|
||||
var source = read(testFileName + '.js');
|
||||
var output = read(testFileName + '.output.js');
|
||||
|
||||
var transform = require('../../transforms/' + transformName);
|
||||
expect(
|
||||
(transform({path, source}, {jscodeshift}, options || {}) || '').trim()
|
||||
).toEqual(
|
||||
output.trim()
|
||||
);
|
||||
}
|
||||
|
||||
describe('Transform Tests', () => {
|
||||
|
||||
it('transforms the "findDOMNode" tests correctly', () => {
|
||||
test('findDOMNode', 'findDOMNode-test');
|
||||
});
|
||||
|
||||
it('transforms the "pure-render-mixin" tests correctly', () => {
|
||||
test('pure-render-mixin', 'pure-render-mixin-test');
|
||||
|
||||
test('pure-render-mixin', 'pure-render-mixin-test2');
|
||||
|
||||
test('pure-render-mixin', 'pure-render-mixin-test3');
|
||||
|
||||
test('pure-render-mixin', 'pure-render-mixin-test4', {
|
||||
'mixin-name': 'ReactComponentWithPureRenderMixin',
|
||||
});
|
||||
});
|
||||
|
||||
it('transforms the "class" tests correctly', () => {
|
||||
test('class', 'class-test');
|
||||
|
||||
test('class', 'class-test2', {
|
||||
'super-class': false
|
||||
});
|
||||
|
||||
test('class', 'class-test3');
|
||||
|
||||
});
|
||||
|
||||
it('transforms exports class', () => {
|
||||
test('class', 'export-default-class-test');
|
||||
});
|
||||
|
||||
});
|
||||
133
packages/react-codemod/test/class-test.js
vendored
133
packages/react-codemod/test/class-test.js
vendored
@@ -1,133 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var Relay = require('Relay');
|
||||
|
||||
var Image = require('Image.react');
|
||||
|
||||
/*
|
||||
* Multiline
|
||||
*/
|
||||
var MyComponent = React.createClass({
|
||||
getInitialState: function() {
|
||||
var x = this.props.foo;
|
||||
return {
|
||||
heyoo: 23,
|
||||
};
|
||||
},
|
||||
|
||||
foo: function() {
|
||||
this.setState({heyoo: 24});
|
||||
}
|
||||
});
|
||||
|
||||
// Class comment
|
||||
var MyComponent2 = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {a: 1};
|
||||
},
|
||||
foo: function() {
|
||||
pass(this.foo);
|
||||
this.forceUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
var MyComponent3 = React.createClass({
|
||||
statics: {
|
||||
someThing: 10,
|
||||
foo: function() {},
|
||||
},
|
||||
propTypes: {
|
||||
highlightEntities: React.PropTypes.bool,
|
||||
linkifyEntities: React.PropTypes.bool,
|
||||
text: React.PropTypes.shape({
|
||||
text: React.PropTypes.string,
|
||||
ranges: React.PropTypes.array
|
||||
}).isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
foo();
|
||||
return {
|
||||
linkifyEntities: true,
|
||||
highlightEntities: false
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
this.props.foo();
|
||||
return {
|
||||
heyoo: 23,
|
||||
};
|
||||
},
|
||||
|
||||
_renderText: function(text) {
|
||||
return <Text text={text} />;
|
||||
},
|
||||
|
||||
_renderImageRange: function(text, range) {
|
||||
var image = range.image;
|
||||
if (image) {
|
||||
return (
|
||||
<Image
|
||||
src={image.uri}
|
||||
height={image.height / image.scale}
|
||||
width={image.width / image.scale}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
autobindMe: function() {},
|
||||
dontAutobindMe: function() {},
|
||||
|
||||
// Function comment
|
||||
_renderRange: function(text, range) {
|
||||
var self = this;
|
||||
|
||||
self.dontAutobindMe();
|
||||
call(self.autobindMe);
|
||||
|
||||
var type = rage.type;
|
||||
var {highlightEntities} = this.props;
|
||||
|
||||
if (type === 'ImageAtRange') {
|
||||
return this._renderImageRange(text, range);
|
||||
}
|
||||
|
||||
if (this.props.linkifyEntities) {
|
||||
text =
|
||||
<Link href={usersURI}>
|
||||
{text}
|
||||
</Link>;
|
||||
} else {
|
||||
text = <span>{text}</span>;
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
|
||||
/* This is a comment */
|
||||
render: function() {
|
||||
var content = this.props.text;
|
||||
return (
|
||||
<BaseText
|
||||
{...this.props}
|
||||
textRenderer={this._renderText}
|
||||
rangeRenderer={this._renderRange}
|
||||
text={content.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var MyComponent4 = React.createClass({
|
||||
foo: callMeMaybe(),
|
||||
render: function() {},
|
||||
});
|
||||
|
||||
module.exports = Relay.createContainer(MyComponent, {
|
||||
queries: {
|
||||
me: Relay.graphql`this is not graphql`,
|
||||
}
|
||||
});
|
||||
@@ -1,144 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var Relay = require('Relay');
|
||||
|
||||
var Image = require('Image.react');
|
||||
|
||||
/*
|
||||
* Multiline
|
||||
*/
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
var x = props.foo;
|
||||
|
||||
this.state = {
|
||||
heyoo: 23,
|
||||
};
|
||||
}
|
||||
|
||||
foo() {
|
||||
this.setState({heyoo: 24});
|
||||
}
|
||||
}
|
||||
|
||||
// Class comment
|
||||
class MyComponent2 extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.foo = this.foo.bind(this);
|
||||
}
|
||||
|
||||
foo() {
|
||||
pass(this.foo);
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
MyComponent2.defaultProps = {a: 1};
|
||||
|
||||
class MyComponent3 extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this._renderRange = this._renderRange.bind(this);
|
||||
this._renderText = this._renderText.bind(this);
|
||||
this.autobindMe = this.autobindMe.bind(this);
|
||||
props.foo();
|
||||
|
||||
this.state = {
|
||||
heyoo: 23,
|
||||
};
|
||||
}
|
||||
|
||||
_renderText(text) {
|
||||
return <Text text={text} />;
|
||||
}
|
||||
|
||||
_renderImageRange(text, range) {
|
||||
var image = range.image;
|
||||
if (image) {
|
||||
return (
|
||||
<Image
|
||||
src={image.uri}
|
||||
height={image.height / image.scale}
|
||||
width={image.width / image.scale}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
autobindMe() {}
|
||||
dontAutobindMe() {}
|
||||
|
||||
// Function comment
|
||||
_renderRange(text, range) {
|
||||
var self = this;
|
||||
|
||||
self.dontAutobindMe();
|
||||
call(self.autobindMe);
|
||||
|
||||
var type = rage.type;
|
||||
var {highlightEntities} = this.props;
|
||||
|
||||
if (type === 'ImageAtRange') {
|
||||
return this._renderImageRange(text, range);
|
||||
}
|
||||
|
||||
if (this.props.linkifyEntities) {
|
||||
text =
|
||||
<Link href={usersURI}>
|
||||
{text}
|
||||
</Link>;
|
||||
} else {
|
||||
text = <span>{text}</span>;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/* This is a comment */
|
||||
render() {
|
||||
var content = this.props.text;
|
||||
return (
|
||||
<BaseText
|
||||
{...this.props}
|
||||
textRenderer={this._renderText}
|
||||
rangeRenderer={this._renderRange}
|
||||
text={content.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MyComponent3.defaultProps = function() {
|
||||
foo();
|
||||
return {
|
||||
linkifyEntities: true,
|
||||
highlightEntities: false
|
||||
};
|
||||
}();
|
||||
|
||||
MyComponent3.foo = function() {};
|
||||
|
||||
MyComponent3.propTypes = {
|
||||
highlightEntities: React.PropTypes.bool,
|
||||
linkifyEntities: React.PropTypes.bool,
|
||||
text: React.PropTypes.shape({
|
||||
text: React.PropTypes.string,
|
||||
ranges: React.PropTypes.array
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
MyComponent3.someThing = 10;
|
||||
|
||||
var MyComponent4 = React.createClass({
|
||||
foo: callMeMaybe(),
|
||||
render: function() {},
|
||||
});
|
||||
|
||||
module.exports = Relay.createContainer(MyComponent, {
|
||||
queries: {
|
||||
me: Relay.graphql`this is not graphql`,
|
||||
}
|
||||
});
|
||||
33
packages/react-codemod/test/class-test2.js
vendored
33
packages/react-codemod/test/class-test2.js
vendored
@@ -1,33 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var IdontNeedAParent = React.createClass({
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var ButIDo = React.createClass({
|
||||
foo: function() {
|
||||
this.setState({banana: '?'});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var IAccessProps = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
relayReleaseDate: this.props.soon,
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
class IdontNeedAParent {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
class ButIDo extends React.Component {
|
||||
foo() {
|
||||
this.setState({banana: '?'});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
class IAccessProps {
|
||||
constructor(props) {
|
||||
this.state = {
|
||||
relayReleaseDate: props.soon,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return
|
||||
}
|
||||
}
|
||||
20
packages/react-codemod/test/class-test3.js
vendored
20
packages/react-codemod/test/class-test3.js
vendored
@@ -1,20 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
// Comment
|
||||
module.exports = React.createClass({
|
||||
propTypes: {
|
||||
foo: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
// Comment
|
||||
module.exports = class extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
foo: 'bar',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.propTypes = {
|
||||
foo: React.PropTypes.bool,
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'React';
|
||||
|
||||
export default React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
foo: 'bar',
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
foo: React.PropTypes.string,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'React';
|
||||
|
||||
export default class extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
foo: 'bar',
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
foo: React.PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
34
packages/react-codemod/test/findDOMNode-test.js
vendored
34
packages/react-codemod/test/findDOMNode-test.js
vendored
@@ -1,34 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var Composer = React.createClass({
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
this.getDOMNode();
|
||||
return foo(this.refs.input.getDOMNode());
|
||||
},
|
||||
|
||||
foo: function() {
|
||||
var ref = 'foo';
|
||||
var element = this.refs[ref];
|
||||
var domNode = element.getDOMNode();
|
||||
},
|
||||
|
||||
bar: function() {
|
||||
var thing = this.refs.foo;
|
||||
thing.getDOMNode();
|
||||
},
|
||||
|
||||
foobar: function() {
|
||||
passThisOn(this.refs.main.refs.list.getDOMNode());
|
||||
}
|
||||
});
|
||||
|
||||
var SomeDialog = React.createClass({
|
||||
render: function() {
|
||||
call(this.refs.SomeThing);
|
||||
return (
|
||||
<div />
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var Composer = React.createClass({
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
React.findDOMNode(this);
|
||||
return foo(React.findDOMNode(this.refs.input));
|
||||
},
|
||||
|
||||
foo: function() {
|
||||
var ref = 'foo';
|
||||
var element = this.refs[ref];
|
||||
var domNode = React.findDOMNode(element);
|
||||
},
|
||||
|
||||
bar: function() {
|
||||
var thing = this.refs.foo;
|
||||
React.findDOMNode(thing);
|
||||
},
|
||||
|
||||
foobar: function() {
|
||||
passThisOn(React.findDOMNode(this.refs.main.refs.list));
|
||||
}
|
||||
});
|
||||
|
||||
var SomeDialog = React.createClass({
|
||||
render: function() {
|
||||
call(this.refs.SomeThing);
|
||||
return (
|
||||
<div />
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
var React = require('react/addons');
|
||||
|
||||
var PureRenderMixin = React.addons.PureRenderMixin;
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyMixedComponent = React.createClass({
|
||||
mixins: [PureRenderMixin, SomeOtherMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyFooComponent = React.createClass({
|
||||
mixins: [PureRenderMixin, SomeOtherMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
},
|
||||
|
||||
foo: function() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var MyStupidComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
shouldComponentUpdate: function() {
|
||||
return !!'wtf is this doing here?';
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
@@ -1,55 +0,0 @@
|
||||
var React = require('react/addons');
|
||||
|
||||
var PureRenderMixin = React.addons.PureRenderMixin;
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return React.addons.shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyMixedComponent = React.createClass({
|
||||
mixins: [SomeOtherMixin],
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return React.addons.shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyFooComponent = React.createClass({
|
||||
mixins: [SomeOtherMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
},
|
||||
|
||||
foo: function() {
|
||||
|
||||
},
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return React.addons.shallowCompare(this, nextProps, nextState);
|
||||
}
|
||||
});
|
||||
|
||||
var MyStupidComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
shouldComponentUpdate: function() {
|
||||
return !!'wtf is this doing here?';
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
@@ -1,13 +0,0 @@
|
||||
var React = require('react/addons');
|
||||
|
||||
var PureRenderMixin = React.addons.PureRenderMixin;
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
@@ -1,13 +0,0 @@
|
||||
var React = require('react/addons');
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return React.addons.shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
@@ -1,14 +0,0 @@
|
||||
var React = require('react/addons');
|
||||
|
||||
var Foo = 'Foo';
|
||||
var PureRenderMixin = React.addons.PureRenderMixin;
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
@@ -1,15 +0,0 @@
|
||||
var React = require('react/addons');
|
||||
|
||||
var Foo = 'Foo';
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return React.addons.shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
@@ -1,12 +0,0 @@
|
||||
var React = require('React');
|
||||
var ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
mixins: [ReactComponentWithPureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
@@ -1,13 +0,0 @@
|
||||
var React = require('React');
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return React.addons.shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
555
packages/react-codemod/transforms/class.js
vendored
555
packages/react-codemod/transforms/class.js
vendored
@@ -1,555 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = (file, api, options) => {
|
||||
const j = api.jscodeshift;
|
||||
|
||||
require('./utils/array-polyfills');
|
||||
const ReactUtils = require('./utils/ReactUtils')(j);
|
||||
|
||||
const printOptions =
|
||||
options.printOptions || {quote: 'single', trailingComma: true};
|
||||
const root = j(file.source);
|
||||
|
||||
const AUTOBIND_IGNORE_KEYS = {
|
||||
componentDidMount: true,
|
||||
componentDidUpdate: true,
|
||||
componentWillReceiveProps: true,
|
||||
componentWillMount: true,
|
||||
componentWillUpdate: true,
|
||||
componentWillUnmount: true,
|
||||
getDefaultProps: true,
|
||||
getInitialState: true,
|
||||
render: true,
|
||||
shouldComponentUpdate: true,
|
||||
};
|
||||
|
||||
const BASE_COMPONENT_METHODS = ['setState', 'forceUpdate'];
|
||||
|
||||
const DEFAULT_PROPS_FIELD = 'getDefaultProps';
|
||||
const DEFAULT_PROPS_KEY = 'defaultProps';
|
||||
const GET_INITIAL_STATE_FIELD = 'getInitialState';
|
||||
|
||||
const DEPRECATED_APIS = [
|
||||
'getDOMNode',
|
||||
'isMounted',
|
||||
'replaceProps',
|
||||
'replaceState',
|
||||
'setProps',
|
||||
];
|
||||
|
||||
const STATIC_KEY = 'statics';
|
||||
|
||||
const STATIC_KEYS = {
|
||||
childContextTypes: true,
|
||||
contextTypes: true,
|
||||
displayName: true,
|
||||
propTypes: true,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Checks if the module uses mixins or accesses deprecated APIs.
|
||||
const checkDeprecatedAPICalls = classPath =>
|
||||
DEPRECATED_APIS.reduce(
|
||||
(acc, name) =>
|
||||
acc + j(classPath)
|
||||
.find(j.Identifier, {name})
|
||||
.size(),
|
||||
0
|
||||
) > 0;
|
||||
|
||||
const callsDeprecatedAPIs = classPath => {
|
||||
if (checkDeprecatedAPICalls(classPath)) {
|
||||
console.log(
|
||||
file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
|
||||
'was skipped because of deprecated API calls. Remove calls to ' +
|
||||
DEPRECATED_APIS.join(', ') + ' in your React component and re-run ' +
|
||||
'this script.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const canConvertToClass = classPath => {
|
||||
const specPath = ReactUtils.getReactCreateClassSpec(classPath);
|
||||
const invalidProperties = specPath.properties.filter(prop => (
|
||||
!prop.key.name || (
|
||||
!STATIC_KEYS[prop.key.name] &&
|
||||
STATIC_KEY != prop.key.name &&
|
||||
!filterDefaultPropsField(prop) &&
|
||||
!filterGetInitialStateField(prop) &&
|
||||
!isFunctionExpression(prop)
|
||||
)
|
||||
));
|
||||
|
||||
if (invalidProperties.length) {
|
||||
const invalidText = invalidProperties
|
||||
.map(prop => prop.key.name ? prop.key.name : prop.key)
|
||||
.join(', ');
|
||||
console.log(
|
||||
file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
|
||||
'was skipped because of invalid field(s) `' + invalidText + '` on ' +
|
||||
'the React component. Remove any right-hand-side expressions that ' +
|
||||
'are not simple, like: `componentWillUpdate: createWillUpdate()` or ' +
|
||||
'`render: foo ? renderA : renderB`.'
|
||||
);
|
||||
}
|
||||
return !invalidProperties.length;
|
||||
};
|
||||
|
||||
const hasMixins = classPath => {
|
||||
if (ReactUtils.hasMixins(classPath)) {
|
||||
console.log(
|
||||
file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' +
|
||||
'was skipped because of mixins.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
const createFindPropFn = prop => property => (
|
||||
property.key &&
|
||||
property.key.type === 'Identifier' &&
|
||||
property.key.name === prop
|
||||
);
|
||||
|
||||
const filterDefaultPropsField = node =>
|
||||
createFindPropFn(DEFAULT_PROPS_FIELD)(node);
|
||||
|
||||
const filterGetInitialStateField = node =>
|
||||
createFindPropFn(GET_INITIAL_STATE_FIELD)(node);
|
||||
|
||||
const findGetDefaultProps = specPath =>
|
||||
specPath.properties.find(createFindPropFn(DEFAULT_PROPS_FIELD));
|
||||
|
||||
const findGetInitialState = specPath =>
|
||||
specPath.properties.find(createFindPropFn(GET_INITIAL_STATE_FIELD));
|
||||
|
||||
// This is conservative; only check for `setState` and `forceUpdate` literals
|
||||
// instead of also checking which objects they are called on.
|
||||
const shouldExtendReactComponent = classPath =>
|
||||
BASE_COMPONENT_METHODS.some(name => (
|
||||
j(classPath)
|
||||
.find(j.Identifier, {name})
|
||||
.size() > 0
|
||||
));
|
||||
|
||||
const withComments = (to, from) => {
|
||||
to.comments = from.comments;
|
||||
return to;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Collectors
|
||||
const isFunctionExpression = node => (
|
||||
node.key &&
|
||||
node.key.type === 'Identifier' &&
|
||||
node.value &&
|
||||
node.value.type === 'FunctionExpression'
|
||||
);
|
||||
|
||||
const collectStatics = specPath => {
|
||||
const statics = specPath.properties.find(createFindPropFn('statics'));
|
||||
const staticsObject =
|
||||
(statics && statics.value && statics.value.properties) || [];
|
||||
|
||||
const getDefaultProps = findGetDefaultProps(specPath);
|
||||
if (getDefaultProps) {
|
||||
staticsObject.push(createDefaultProps(getDefaultProps));
|
||||
}
|
||||
|
||||
return (
|
||||
staticsObject.concat(specPath.properties.filter(property =>
|
||||
property.key && STATIC_KEYS[property.key.name]
|
||||
))
|
||||
.sort((a, b) => a.key.name < b.key.name)
|
||||
);
|
||||
};
|
||||
|
||||
const collectFunctions = specPath => specPath.properties
|
||||
.filter(prop =>
|
||||
!(filterDefaultPropsField(prop) || filterGetInitialStateField(prop))
|
||||
)
|
||||
.filter(isFunctionExpression);
|
||||
|
||||
const findAutobindNamesFor = (subtree, fnNames, literalOrIdentifier) => {
|
||||
const node = literalOrIdentifier;
|
||||
const autobindNames = {};
|
||||
|
||||
j(subtree)
|
||||
.find(j.MemberExpression, {
|
||||
object: node.name ? {
|
||||
type: node.type,
|
||||
name: node.name,
|
||||
} : {type: node.type},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
},
|
||||
})
|
||||
.filter(path => path.value.property && fnNames[path.value.property.name])
|
||||
.filter(path => {
|
||||
const call = path.parent.value;
|
||||
return !(
|
||||
call &&
|
||||
call.type === 'CallExpression' &&
|
||||
call.callee.type === 'MemberExpression' &&
|
||||
call.callee.object.type === node.type &&
|
||||
call.callee.object.name === node.name &&
|
||||
call.callee.property.type === 'Identifier' &&
|
||||
call.callee.property.name === path.value.property.name
|
||||
);
|
||||
})
|
||||
.forEach(path => autobindNames[path.value.property.name] = true);
|
||||
|
||||
return Object.keys(autobindNames);
|
||||
};
|
||||
|
||||
const collectAutoBindFunctions = (functions, classPath) => {
|
||||
const fnNames = {};
|
||||
functions
|
||||
.filter(fn => !AUTOBIND_IGNORE_KEYS[fn.key.name])
|
||||
.forEach(fn => fnNames[fn.key.name] = true);
|
||||
|
||||
const autobindNames = {};
|
||||
const add = name => autobindNames[name] = true;
|
||||
|
||||
// Find `this.<foo>`
|
||||
findAutobindNamesFor(classPath, fnNames, j.thisExpression()).forEach(add);
|
||||
|
||||
// Find `self.<foo>` if `self = this`
|
||||
j(classPath)
|
||||
.findVariableDeclarators()
|
||||
.filter(path => (
|
||||
path.value.id.type === 'Identifier' &&
|
||||
path.value.init &&
|
||||
path.value.init.type === 'ThisExpression'
|
||||
))
|
||||
.forEach(path =>
|
||||
findAutobindNamesFor(
|
||||
j(path).closest(j.FunctionExpression).get(),
|
||||
fnNames,
|
||||
path.value.id
|
||||
).forEach(add)
|
||||
);
|
||||
|
||||
return Object.keys(autobindNames).sort();
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Boom!
|
||||
const createMethodDefinition = fn =>
|
||||
withComments(j.methodDefinition(
|
||||
'method',
|
||||
fn.key,
|
||||
fn.value
|
||||
), fn);
|
||||
|
||||
const createBindAssignment = name =>
|
||||
j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
'=',
|
||||
j.memberExpression(
|
||||
j.thisExpression(),
|
||||
j.identifier(name),
|
||||
false
|
||||
),
|
||||
j.callExpression(
|
||||
j.memberExpression(
|
||||
j.memberExpression(
|
||||
j.thisExpression(),
|
||||
j.identifier(name),
|
||||
false
|
||||
),
|
||||
j.identifier('bind'),
|
||||
false
|
||||
),
|
||||
[j.thisExpression()]
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const createSuperCall = shouldAddSuperCall =>
|
||||
!shouldAddSuperCall ?
|
||||
[] :
|
||||
[
|
||||
j.expressionStatement(
|
||||
j.callExpression(
|
||||
j.identifier('super'),
|
||||
[j.identifier('props'), j.identifier('context')]
|
||||
)
|
||||
),
|
||||
];
|
||||
|
||||
const updatePropsAccess = getInitialState =>
|
||||
getInitialState ?
|
||||
j(getInitialState)
|
||||
.find(j.MemberExpression, {
|
||||
object: {
|
||||
type: 'ThisExpression',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'props',
|
||||
},
|
||||
})
|
||||
.forEach(path => j(path).replaceWith(j.identifier('props')))
|
||||
.size() > 0 :
|
||||
false;
|
||||
|
||||
const inlineGetInitialState = getInitialState => {
|
||||
if (!getInitialState) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return getInitialState.value.body.body.map(statement => {
|
||||
if (statement.type === 'ReturnStatement') {
|
||||
return j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
'=',
|
||||
j.memberExpression(
|
||||
j.thisExpression(),
|
||||
j.identifier('state'),
|
||||
false
|
||||
),
|
||||
statement.argument
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return statement;
|
||||
});
|
||||
};
|
||||
|
||||
const createConstructorArgs = (shouldAddSuperClass, hasPropsAccess) => {
|
||||
if (shouldAddSuperClass) {
|
||||
return [j.identifier('props'), j.identifier('context')];
|
||||
} else if (hasPropsAccess) {
|
||||
return [j.identifier('props')];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const createConstructor = (
|
||||
getInitialState,
|
||||
autobindFunctions,
|
||||
shouldAddSuperClass
|
||||
) => {
|
||||
if (!getInitialState && !autobindFunctions.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const hasPropsAccess = updatePropsAccess(getInitialState);
|
||||
return [
|
||||
createMethodDefinition({
|
||||
key: j.identifier('constructor'),
|
||||
value: j.functionExpression(
|
||||
null,
|
||||
createConstructorArgs(shouldAddSuperClass, hasPropsAccess),
|
||||
j.blockStatement(
|
||||
[].concat(
|
||||
createSuperCall(shouldAddSuperClass),
|
||||
autobindFunctions.map(createBindAssignment),
|
||||
inlineGetInitialState(getInitialState)
|
||||
)
|
||||
)
|
||||
),
|
||||
}),
|
||||
];
|
||||
};
|
||||
|
||||
const createESClass = (
|
||||
name,
|
||||
properties,
|
||||
getInitialState,
|
||||
autobindFunctions,
|
||||
comments,
|
||||
shouldAddSuperClass
|
||||
) =>
|
||||
withComments(j.classDeclaration(
|
||||
name ? j.identifier(name) : null,
|
||||
j.classBody(
|
||||
[].concat(
|
||||
createConstructor(
|
||||
getInitialState,
|
||||
autobindFunctions,
|
||||
shouldAddSuperClass
|
||||
),
|
||||
properties
|
||||
)
|
||||
),
|
||||
shouldAddSuperClass ? j.memberExpression(
|
||||
j.identifier('React'),
|
||||
j.identifier('Component'),
|
||||
false
|
||||
) : null
|
||||
), {comments});
|
||||
|
||||
const createStaticAssignment = (name, staticProperty) =>
|
||||
withComments(j.expressionStatement(
|
||||
j.assignmentExpression(
|
||||
'=',
|
||||
j.memberExpression(
|
||||
name,
|
||||
j.identifier(staticProperty.key.name),
|
||||
false
|
||||
),
|
||||
staticProperty.value
|
||||
)
|
||||
), staticProperty);
|
||||
|
||||
const createStaticAssignmentExpressions = (name, statics) =>
|
||||
statics.map(staticProperty => createStaticAssignment(name, staticProperty));
|
||||
|
||||
const createStaticClassProperty = staticProperty =>
|
||||
withComments(j.classProperty(
|
||||
j.identifier(staticProperty.key.name),
|
||||
staticProperty.value,
|
||||
null,
|
||||
true
|
||||
), staticProperty);
|
||||
|
||||
const createStaticClassProperties = statics =>
|
||||
statics.map(createStaticClassProperty);
|
||||
|
||||
const hasSingleReturnStatement = value => (
|
||||
value.type === 'FunctionExpression' &&
|
||||
value.body &&
|
||||
value.body.type === 'BlockStatement' &&
|
||||
value.body.body &&
|
||||
value.body.body.length === 1 &&
|
||||
value.body.body[0].type === 'ReturnStatement' &&
|
||||
value.body.body[0].argument &&
|
||||
value.body.body[0].argument.type === 'ObjectExpression'
|
||||
);
|
||||
|
||||
const createDefaultPropsValue = value => {
|
||||
if (hasSingleReturnStatement(value)) {
|
||||
return value.body.body[0].argument;
|
||||
} else {
|
||||
return j.callExpression(
|
||||
value,
|
||||
[]
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const createDefaultProps = prop =>
|
||||
withComments(
|
||||
j.property(
|
||||
'init',
|
||||
j.identifier(DEFAULT_PROPS_KEY),
|
||||
createDefaultPropsValue(prop.value)
|
||||
),
|
||||
prop
|
||||
);
|
||||
|
||||
const getComments = classPath => {
|
||||
if (classPath.value.comments) {
|
||||
return classPath.value.comments;
|
||||
}
|
||||
const declaration = j(classPath).closest(j.VariableDeclaration);
|
||||
if (declaration.size()) {
|
||||
return declaration.get().value.comments;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const createModuleExportsMemberExpression = () =>
|
||||
j.memberExpression(
|
||||
j.identifier('module'),
|
||||
j.identifier('exports'),
|
||||
false
|
||||
);
|
||||
|
||||
const updateToClass = (classPath, shouldExtend, type) => {
|
||||
const specPath = ReactUtils.getReactCreateClassSpec(classPath);
|
||||
const name = ReactUtils.getComponentName(classPath);
|
||||
const statics = collectStatics(specPath);
|
||||
const functions = collectFunctions(specPath);
|
||||
const comments = getComments(classPath);
|
||||
|
||||
const autobindFunctions = collectAutoBindFunctions(functions, classPath);
|
||||
const getInitialState = findGetInitialState(specPath);
|
||||
|
||||
const staticName =
|
||||
name ? j.identifier(name) : createModuleExportsMemberExpression();
|
||||
|
||||
var path;
|
||||
if (type == 'moduleExports' || type == 'exportDefault') {
|
||||
path = ReactUtils.findReactCreateClassCallExpression(classPath);
|
||||
} else {
|
||||
path = j(classPath).closest(j.VariableDeclaration);
|
||||
}
|
||||
|
||||
const properties =
|
||||
(type == 'exportDefault') ? createStaticClassProperties(statics) : [];
|
||||
|
||||
path.replaceWith(
|
||||
createESClass(
|
||||
name,
|
||||
properties.concat(functions.map(createMethodDefinition)),
|
||||
getInitialState,
|
||||
autobindFunctions,
|
||||
comments,
|
||||
shouldExtend || shouldExtendReactComponent(classPath)
|
||||
)
|
||||
);
|
||||
|
||||
if (type == 'moduleExports' || type == 'var') {
|
||||
const staticAssignments = createStaticAssignmentExpressions(
|
||||
staticName,
|
||||
statics
|
||||
);
|
||||
if (type == 'moduleExports') {
|
||||
root.get().value.program.body.push(...staticAssignments);
|
||||
} else {
|
||||
path.insertAfter(staticAssignments.reverse());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (
|
||||
!options['explicit-require'] || ReactUtils.hasReact(root)
|
||||
) {
|
||||
const apply = (path, type) =>
|
||||
path
|
||||
.filter(hasMixins)
|
||||
.filter(callsDeprecatedAPIs)
|
||||
.filter(canConvertToClass)
|
||||
.forEach(classPath => updateToClass(
|
||||
classPath,
|
||||
!options['super-class'],
|
||||
type
|
||||
));
|
||||
|
||||
const didTransform = (
|
||||
apply(ReactUtils.findReactCreateClass(root), 'var')
|
||||
.size() +
|
||||
apply(ReactUtils.findReactCreateClassModuleExports(root), 'moduleExports')
|
||||
.size() +
|
||||
apply(ReactUtils.findReactCreateClassExportDefault(root), 'exportDefault')
|
||||
.size()
|
||||
) > 0;
|
||||
|
||||
if (didTransform) {
|
||||
return root.toSource(printOptions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
144
packages/react-codemod/transforms/findDOMNode.js
vendored
144
packages/react-codemod/transforms/findDOMNode.js
vendored
@@ -1,144 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function getDOMNodeToFindDOMNode(file, api, options) {
|
||||
const j = api.jscodeshift;
|
||||
|
||||
require('./utils/array-polyfills');
|
||||
const ReactUtils = require('./utils/ReactUtils')(j);
|
||||
|
||||
const printOptions =
|
||||
options.printOptions || {quote: 'single', trailingComma: true};
|
||||
const root = j(file.source);
|
||||
|
||||
const createReactFindDOMNodeCall = arg => j.callExpression(
|
||||
j.memberExpression(
|
||||
j.identifier('React'),
|
||||
j.identifier('findDOMNode'),
|
||||
false
|
||||
),
|
||||
[arg]
|
||||
);
|
||||
|
||||
const updateRefCall = (path, refName) => {
|
||||
j(path)
|
||||
.find(j.CallExpression, {
|
||||
callee: {
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: refName,
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'getDOMNode',
|
||||
},
|
||||
},
|
||||
})
|
||||
.forEach(callPath => j(callPath).replaceWith(
|
||||
createReactFindDOMNodeCall(j.identifier(refName))
|
||||
));
|
||||
};
|
||||
|
||||
const updateToFindDOMNode = classPath => {
|
||||
var sum = 0;
|
||||
|
||||
// this.getDOMNode()
|
||||
sum += j(classPath)
|
||||
.find(j.CallExpression, {
|
||||
callee: {
|
||||
object: {
|
||||
type: 'ThisExpression',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'getDOMNode',
|
||||
},
|
||||
},
|
||||
})
|
||||
.forEach(path => j(path).replaceWith(
|
||||
createReactFindDOMNodeCall(j.thisExpression())
|
||||
))
|
||||
.size();
|
||||
|
||||
// this.refs.xxx.getDOMNode() or this.refs.xxx.refs.yyy.getDOMNode()
|
||||
sum += j(classPath)
|
||||
.find(j.MemberExpression, {
|
||||
object: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'ThisExpression',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'refs',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.closest(j.CallExpression)
|
||||
.filter(path => (
|
||||
path.value.callee.property &&
|
||||
path.value.callee.property.type === 'Identifier' &&
|
||||
path.value.callee.property.name === 'getDOMNode'
|
||||
))
|
||||
.forEach(path => j(path).replaceWith(
|
||||
createReactFindDOMNodeCall(path.value.callee.object)
|
||||
))
|
||||
.size();
|
||||
|
||||
// someVariable.getDOMNode() wherre `someVariable = this.refs.xxx`
|
||||
sum += j(classPath)
|
||||
.findVariableDeclarators()
|
||||
.filter(path => {
|
||||
const init = path.value.init;
|
||||
const value = init && init.object;
|
||||
return (
|
||||
value &&
|
||||
value.type === 'MemberExpression' &&
|
||||
value.object &&
|
||||
value.object.type === 'ThisExpression' &&
|
||||
value.property &&
|
||||
value.property.type === 'Identifier' &&
|
||||
value.property.name === 'refs' &&
|
||||
init.property &&
|
||||
init.property.type === 'Identifier'
|
||||
);
|
||||
})
|
||||
.forEach(path => j(path)
|
||||
.closest(j.FunctionExpression)
|
||||
.forEach(fnPath => updateRefCall(fnPath, path.value.id.name))
|
||||
)
|
||||
.size();
|
||||
|
||||
return sum > 0;
|
||||
};
|
||||
|
||||
if (
|
||||
!options['explicit-require'] ||
|
||||
ReactUtils.hasReact(root)
|
||||
) {
|
||||
const didTransform = ReactUtils
|
||||
.findReactCreateClass(root)
|
||||
.filter(updateToFindDOMNode)
|
||||
.size() > 0;
|
||||
|
||||
if (didTransform) {
|
||||
return root.toSource(printOptions);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = getDOMNodeToFindDOMNode;
|
||||
@@ -1,188 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function removePureRenderMixin(file, api, options) {
|
||||
const j = api.jscodeshift;
|
||||
|
||||
require('./utils/array-polyfills');
|
||||
const ReactUtils = require('./utils/ReactUtils')(j);
|
||||
|
||||
const printOptions =
|
||||
options.printOptions || {quote: 'single', trailingComma: true};
|
||||
const root = j(file.source);
|
||||
|
||||
const PURE_RENDER_MIXIN = options['mixin-name'] || 'PureRenderMixin';
|
||||
const SHOULD_COMPONENT_UPDATE = 'shouldComponentUpdate';
|
||||
const NEXT_PROPS = 'nextProps';
|
||||
const NEXT_STATE = 'nextState';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// shouldComponentUpdate
|
||||
const createShouldComponentUpdateFunction = () =>
|
||||
j.functionExpression(
|
||||
null,
|
||||
[j.identifier(NEXT_PROPS), j.identifier(NEXT_STATE)],
|
||||
j.blockStatement([
|
||||
j.returnStatement(
|
||||
j.callExpression(
|
||||
j.memberExpression(
|
||||
j.identifier('React'),
|
||||
j.memberExpression(
|
||||
j.identifier('addons'),
|
||||
j.identifier('shallowCompare'),
|
||||
false
|
||||
),
|
||||
false
|
||||
),
|
||||
[
|
||||
j.thisExpression(),
|
||||
j.identifier(NEXT_PROPS),
|
||||
j.identifier(NEXT_STATE),
|
||||
]
|
||||
)
|
||||
),
|
||||
])
|
||||
);
|
||||
|
||||
const createShouldComponentUpdateProperty = () =>
|
||||
j.property(
|
||||
'init',
|
||||
j.identifier(SHOULD_COMPONENT_UPDATE),
|
||||
createShouldComponentUpdateFunction()
|
||||
);
|
||||
|
||||
const hasShouldComponentUpdate = classPath =>
|
||||
ReactUtils.getReactCreateClassSpec(classPath)
|
||||
.properties.every(property =>
|
||||
property.key.name !== SHOULD_COMPONENT_UPDATE
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mixin related code
|
||||
const isPureRenderMixin = node => (
|
||||
node.type === 'Identifier' &&
|
||||
node.name === PURE_RENDER_MIXIN
|
||||
);
|
||||
|
||||
const hasPureRenderMixin = classPath => {
|
||||
const spec = ReactUtils.getReactCreateClassSpec(classPath);
|
||||
const mixin = spec && spec.properties.find(ReactUtils.isMixinProperty);
|
||||
return mixin && mixin.value.elements.some(isPureRenderMixin);
|
||||
};
|
||||
|
||||
const removeMixin = elements =>
|
||||
j.property(
|
||||
'init',
|
||||
j.identifier('mixins'),
|
||||
j.arrayExpression(
|
||||
elements.filter(element => !isPureRenderMixin(element))
|
||||
)
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Boom!
|
||||
const insertShouldComponentUpdate = properties => {
|
||||
const length = properties.length;
|
||||
const lastProp = properties[length - 1];
|
||||
// I wouldn't dare insert at the bottom if the last function is render
|
||||
if (
|
||||
lastProp.key.type === 'Identifier' &&
|
||||
lastProp.key.name === 'render'
|
||||
) {
|
||||
properties.splice(
|
||||
length - 1,
|
||||
1,
|
||||
createShouldComponentUpdateProperty(),
|
||||
lastProp
|
||||
);
|
||||
} else {
|
||||
properties.push(createShouldComponentUpdateProperty());
|
||||
}
|
||||
return properties;
|
||||
};
|
||||
|
||||
const cleanupReactComponent = classPath => {
|
||||
const spec = ReactUtils.getReactCreateClassSpec(classPath);
|
||||
const properties = spec.properties
|
||||
.map(property => {
|
||||
if (ReactUtils.isMixinProperty(property)) {
|
||||
const elements = property.value.elements;
|
||||
return (elements.length !== 1) ? removeMixin(elements) : null;
|
||||
}
|
||||
return property;
|
||||
})
|
||||
.filter(property => !!property);
|
||||
|
||||
ReactUtils.findReactCreateClassCallExpression(classPath).replaceWith(
|
||||
ReactUtils.createCreateReactClassCallExpression(
|
||||
insertShouldComponentUpdate(properties)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// Remove it if only two or fewer are left:
|
||||
// var PureRenderMixin = React.addons.PureRenderMixin;
|
||||
const hasPureRenderIdentifiers = path =>
|
||||
path.find(j.Identifier, {
|
||||
name: PURE_RENDER_MIXIN,
|
||||
}).size() > 2;
|
||||
|
||||
const deletePureRenderMixin = path => {
|
||||
if (hasPureRenderIdentifiers(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const declaration = path
|
||||
.findVariableDeclarators(PURE_RENDER_MIXIN)
|
||||
.closest(j.VariableDeclaration);
|
||||
|
||||
if (declaration.size > 1) {
|
||||
declaration.forEach(p =>
|
||||
j(p).replaceWith(
|
||||
j.variableDeclaration(
|
||||
'var',
|
||||
p.value.declarations.filter(isPureRenderMixin)
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Let's assume the variable declaration happens at the top level
|
||||
const program = declaration.closest(j.Program).get();
|
||||
const body = program.value.body;
|
||||
const index = body.indexOf(declaration.get().value);
|
||||
if (index !== -1) {
|
||||
body.splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (
|
||||
!options['explicit-require'] ||
|
||||
ReactUtils.hasReact(root)
|
||||
) {
|
||||
const didTransform = ReactUtils
|
||||
.findReactCreateClass(root)
|
||||
.filter(hasPureRenderMixin)
|
||||
.filter(hasShouldComponentUpdate)
|
||||
.forEach(cleanupReactComponent)
|
||||
.size() > 0;
|
||||
|
||||
if (didTransform) {
|
||||
deletePureRenderMixin(root);
|
||||
return root.toSource(printOptions);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = removePureRenderMixin;
|
||||
@@ -1,321 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var CORE_PROPERTIES = [
|
||||
'Children',
|
||||
'Component',
|
||||
'createElement',
|
||||
'cloneElement',
|
||||
'isValidElement',
|
||||
'PropTypes',
|
||||
'createClass',
|
||||
'createFactory',
|
||||
'createMixin',
|
||||
'DOM',
|
||||
'__spread',
|
||||
];
|
||||
|
||||
var DOM_PROPERTIES = [
|
||||
'findDOMNode',
|
||||
'render',
|
||||
'unmountComponentAtNode',
|
||||
'unstable_batchedUpdates',
|
||||
'unstable_renderSubtreeIntoContainer',
|
||||
];
|
||||
|
||||
var DOM_SERVER_PROPERTIES = [
|
||||
'renderToString',
|
||||
'renderToStaticMarkup',
|
||||
];
|
||||
|
||||
function reportError(node, error) {
|
||||
throw new Error(
|
||||
`At ${node.loc.start.line}:${node.loc.start.column}: ${error}`
|
||||
);
|
||||
}
|
||||
|
||||
function isRequire(path, moduleName) {
|
||||
return (
|
||||
path.value.type === 'CallExpression' &&
|
||||
path.value.callee.type === 'Identifier' &&
|
||||
path.value.callee.name === 'require' &&
|
||||
path.value.arguments.length === 1 &&
|
||||
path.value.arguments[0].type === 'Literal' &&
|
||||
path.value.arguments[0].value === moduleName
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = function(file, api) {
|
||||
var j = api.jscodeshift;
|
||||
var root = j(file.source);
|
||||
|
||||
[
|
||||
['React', 'ReactDOM', 'ReactDOMServer'],
|
||||
['react', 'react-dom', 'react-dom/server'],
|
||||
].forEach(function(pair) {
|
||||
var coreModuleName = pair[0];
|
||||
var domModuleName = pair[1];
|
||||
var domServerModuleName = pair[2];
|
||||
|
||||
var domAlreadyDeclared = false;
|
||||
var domServerAlreadyDeclared = false;
|
||||
|
||||
var coreRequireDeclarator;
|
||||
root
|
||||
.find(j.CallExpression)
|
||||
.filter(p => isRequire(p, coreModuleName))
|
||||
.forEach(p => {
|
||||
var name, scope;
|
||||
if (p.parent.value.type === 'VariableDeclarator') {
|
||||
if (p.parent.value.id.type === 'ObjectPattern') {
|
||||
var pattern = p.parent.value.id;
|
||||
var all = pattern.properties.every(function(prop) {
|
||||
if (prop.key.type === 'Identifier') {
|
||||
name = prop.key.name;
|
||||
return CORE_PROPERTIES.indexOf(name) !== -1;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (all) {
|
||||
// var {PropTypes} = require('React'); so leave alone
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (coreRequireDeclarator) {
|
||||
reportError(
|
||||
p.value,
|
||||
'Multiple declarations of React'
|
||||
);
|
||||
}
|
||||
if (p.parent.value.id.type !== 'Identifier') {
|
||||
reportError(
|
||||
p.value,
|
||||
'Unexpected destructuring in require of ' + coreModuleName
|
||||
);
|
||||
}
|
||||
name = p.parent.value.id.name;
|
||||
scope = p.scope.lookup(name);
|
||||
if (scope.declares('ReactDOM')) {
|
||||
console.log('Using existing ReactDOM var in ' + file.path);
|
||||
domAlreadyDeclared = true;
|
||||
}
|
||||
if (scope.declares('ReactDOMServer')) {
|
||||
console.log('Using existing ReactDOMServer var in ' + file.path);
|
||||
domServerAlreadyDeclared = true;
|
||||
}
|
||||
coreRequireDeclarator = p.parent;
|
||||
} else if (p.parent.value.type === 'AssignmentExpression') {
|
||||
if (p.parent.value.left.type !== 'Identifier') {
|
||||
reportError(
|
||||
p.value,
|
||||
'Unexpected destructuring in require of ' + coreModuleName
|
||||
);
|
||||
}
|
||||
name = p.parent.value.left.name;
|
||||
scope = p.scope.lookup(name);
|
||||
var reactBindings = scope.getBindings()[name];
|
||||
if (reactBindings.length !== 1) {
|
||||
throw new Error(
|
||||
'Unexpected number of bindings: ' + reactBindings.length
|
||||
);
|
||||
}
|
||||
coreRequireDeclarator = reactBindings[0].parent;
|
||||
if (coreRequireDeclarator.value.init &&
|
||||
!isRequire(coreRequireDeclarator.get('init'), coreModuleName)) {
|
||||
reportError(
|
||||
coreRequireDeclarator.value,
|
||||
'Unexpected initialization of ' + coreModuleName
|
||||
);
|
||||
}
|
||||
if (scope.declares('ReactDOM')) {
|
||||
console.log('Using existing ReactDOM var in ' + file.path);
|
||||
domAlreadyDeclared = true;
|
||||
}
|
||||
if (scope.declares('ReactDOMServer')) {
|
||||
console.log('Using existing ReactDOMServer var in ' + file.path);
|
||||
domServerAlreadyDeclared = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!coreRequireDeclarator) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!domAlreadyDeclared &&
|
||||
root.find(j.Identifier, {name: 'ReactDOM'}).size() > 0) {
|
||||
throw new Error(
|
||||
'ReactDOM is already defined in a different scope than React'
|
||||
);
|
||||
}
|
||||
if (!domServerAlreadyDeclared &&
|
||||
root.find(j.Identifier, {name: 'ReactDOMServer'}).size() > 0) {
|
||||
throw new Error(
|
||||
'ReactDOMServer is already defined in a different scope than React'
|
||||
);
|
||||
}
|
||||
|
||||
var coreName = coreRequireDeclarator.value.id.name;
|
||||
|
||||
var processed = new Set();
|
||||
var requireAssignments = [];
|
||||
var coreUses = 0;
|
||||
var domUses = 0;
|
||||
var domServerUses = 0;
|
||||
|
||||
root
|
||||
.find(j.Identifier, {name: coreName})
|
||||
.forEach(p => {
|
||||
if (processed.has(p.value)) {
|
||||
// https://github.com/facebook/jscodeshift/issues/36
|
||||
return;
|
||||
}
|
||||
processed.add(p.value);
|
||||
if (p.parent.value.type === 'MemberExpression' ||
|
||||
p.parent.value.type === 'QualifiedTypeIdentifier') {
|
||||
var left;
|
||||
var right;
|
||||
if (p.parent.value.type === 'MemberExpression') {
|
||||
left = p.parent.value.object;
|
||||
right = p.parent.value.property;
|
||||
} else {
|
||||
left = p.parent.value.qualification;
|
||||
right = p.parent.value.id;
|
||||
}
|
||||
if (left === p.value) {
|
||||
// React.foo (or React[foo])
|
||||
if (right.type === 'Identifier') {
|
||||
var name = right.name;
|
||||
if (CORE_PROPERTIES.indexOf(name) !== -1) {
|
||||
coreUses++;
|
||||
} else if (DOM_PROPERTIES.indexOf(name) !== -1) {
|
||||
domUses++;
|
||||
j(p).replaceWith(j.identifier('ReactDOM'));
|
||||
} else if (DOM_SERVER_PROPERTIES.indexOf(name) !== -1) {
|
||||
domServerUses++;
|
||||
j(p).replaceWith(j.identifier('ReactDOMServer'));
|
||||
} else {
|
||||
throw new Error('Unknown property React.' + name);
|
||||
}
|
||||
}
|
||||
} else if (right === p.value) {
|
||||
// foo.React, no need to transform
|
||||
} else {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
} else if (p.parent.value.type === 'VariableDeclarator') {
|
||||
if (p.parent.value.id === p.value) {
|
||||
// var React = ...;
|
||||
} else if (p.parent.value.init === p.value) {
|
||||
// var ... = React;
|
||||
var pattern = p.parent.value.id;
|
||||
if (pattern.type === 'ObjectPattern') {
|
||||
// var {PropTypes} = React;
|
||||
// Most of these cases will just be looking at {PropTypes} so this
|
||||
// is usually a no-op.
|
||||
var coreProperties = [];
|
||||
var domProperties = [];
|
||||
pattern.properties.forEach(function(prop) {
|
||||
if (prop.key.type === 'Identifier') {
|
||||
var key = prop.key.name;
|
||||
if (CORE_PROPERTIES.indexOf(key) !== -1) {
|
||||
coreProperties.push(prop);
|
||||
} else if (DOM_PROPERTIES.indexOf(key) !== -1) {
|
||||
domProperties.push(prop);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unknown property React.' + key + ' while destructuring'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
});
|
||||
var domDeclarator = j.variableDeclarator(
|
||||
j.objectPattern(domProperties),
|
||||
j.identifier('ReactDOM')
|
||||
);
|
||||
if (coreProperties.length && !domProperties.length) {
|
||||
// nothing to do
|
||||
coreUses++;
|
||||
} else if (domProperties.length && !coreProperties.length) {
|
||||
domUses++;
|
||||
j(p.parent).replaceWith(domDeclarator);
|
||||
} else {
|
||||
coreUses++;
|
||||
domUses++;
|
||||
var decl = j(p).closest(j.VariableDeclaration);
|
||||
decl.insertAfter(j.variableDeclaration(
|
||||
decl.get().value.kind,
|
||||
[domDeclarator]
|
||||
));
|
||||
}
|
||||
} else {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
} else {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
} else if (p.parent.value.type === 'AssignmentExpression') {
|
||||
if (p.parent.value.left === p.value) {
|
||||
if (isRequire(p.parent.get('right'), coreModuleName)) {
|
||||
requireAssignments.push(p.parent);
|
||||
} else {
|
||||
reportError(
|
||||
p.parent.value,
|
||||
'Unexpected assignment to ' + coreModuleName
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
} else {
|
||||
reportError(p.value, 'unimplemented ' + p.parent.value.type);
|
||||
}
|
||||
});
|
||||
|
||||
coreUses += root.find(j.JSXElement).size();
|
||||
|
||||
function insertRequire(name, path) {
|
||||
var req = j.callExpression(
|
||||
j.identifier('require'),
|
||||
[j.literal(path)]
|
||||
);
|
||||
requireAssignments.forEach(function(requireAssignment) {
|
||||
requireAssignment.parent.insertAfter(
|
||||
j.expressionStatement(
|
||||
j.assignmentExpression('=', j.identifier(name), req)
|
||||
)
|
||||
);
|
||||
});
|
||||
coreRequireDeclarator.parent.insertAfter(j.variableDeclaration(
|
||||
coreRequireDeclarator.parent.value.kind,
|
||||
[j.variableDeclarator(
|
||||
j.identifier(name),
|
||||
coreRequireDeclarator.value.init ? req : null
|
||||
)]
|
||||
));
|
||||
}
|
||||
|
||||
if (domServerUses > 0 && !domServerAlreadyDeclared) {
|
||||
insertRequire('ReactDOMServer', domServerModuleName);
|
||||
}
|
||||
if (domUses > 0 && !domAlreadyDeclared) {
|
||||
insertRequire('ReactDOM', domModuleName);
|
||||
}
|
||||
if ((domUses > 0 || domServerUses > 0) && coreUses === 0) {
|
||||
j(coreRequireDeclarator).remove();
|
||||
requireAssignments.forEach(r => j(r).remove());
|
||||
}
|
||||
});
|
||||
|
||||
return root.toSource({quote: 'single'});
|
||||
};
|
||||
@@ -1,160 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013-2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function(j) {
|
||||
const REACT_CREATE_CLASS_MEMBER_EXPRESSION = {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
name: 'React',
|
||||
},
|
||||
property: {
|
||||
name: 'createClass',
|
||||
},
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Checks if the file requires a certain module
|
||||
const hasModule = (path, module) =>
|
||||
path
|
||||
.findVariableDeclarators()
|
||||
.filter(j.filters.VariableDeclarator.requiresModule(module))
|
||||
.size() === 1 ||
|
||||
path
|
||||
.find(j.ImportDeclaration, {
|
||||
type: 'ImportDeclaration',
|
||||
source: {
|
||||
type: 'Literal',
|
||||
},
|
||||
})
|
||||
.filter(declarator => declarator.value.source.value === module)
|
||||
.size() === 1;
|
||||
|
||||
const hasReact = path => (
|
||||
hasModule(path, 'React') ||
|
||||
hasModule(path, 'react') ||
|
||||
hasModule(path, 'react/addons')
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Finds all variable declarations that call React.createClass
|
||||
const findReactCreateClassCallExpression = path =>
|
||||
j(path).find(j.CallExpression, {
|
||||
callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION,
|
||||
});
|
||||
|
||||
const findReactCreateClass = path =>
|
||||
path
|
||||
.findVariableDeclarators()
|
||||
.filter(decl => findReactCreateClassCallExpression(decl).size() > 0);
|
||||
|
||||
const findReactCreateClassExportDefault = path =>
|
||||
path.find(j.ExportDefaultDeclaration, {
|
||||
declaration: {
|
||||
type: 'CallExpression',
|
||||
callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION,
|
||||
},
|
||||
});
|
||||
|
||||
const findReactCreateClassModuleExports = path =>
|
||||
path
|
||||
.find(j.AssignmentExpression, {
|
||||
left: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'module',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'exports',
|
||||
},
|
||||
},
|
||||
right: {
|
||||
type: 'CallExpression',
|
||||
callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION,
|
||||
},
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Finds all classes that extend React.Component
|
||||
const findReactES6ClassDeclaration = path =>
|
||||
path
|
||||
.find(j.ClassDeclaration, {
|
||||
superClass: {
|
||||
type: 'MemberExpression',
|
||||
object: {
|
||||
type: 'Identifier',
|
||||
name: 'React',
|
||||
},
|
||||
property: {
|
||||
type: 'Identifier',
|
||||
name: 'Component',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Checks if the React class has mixins
|
||||
const isMixinProperty = property => {
|
||||
const key = property.key;
|
||||
const value = property.value;
|
||||
return (
|
||||
key.name === 'mixins' &&
|
||||
value.type === 'ArrayExpression' &&
|
||||
Array.isArray(value.elements) &&
|
||||
value.elements.length
|
||||
);
|
||||
};
|
||||
|
||||
const hasMixins = classPath => {
|
||||
const spec = getReactCreateClassSpec(classPath);
|
||||
return spec && spec.properties.some(isMixinProperty);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Others
|
||||
const getReactCreateClassSpec = classPath => {
|
||||
var {value} = classPath;
|
||||
const spec = (value.init || value.right || value.declaration).arguments[0];
|
||||
if (spec.type === 'ObjectExpression' && Array.isArray(spec.properties)) {
|
||||
return spec;
|
||||
}
|
||||
};
|
||||
|
||||
const createCreateReactClassCallExpression = properties =>
|
||||
j.callExpression(
|
||||
j.memberExpression(
|
||||
j.identifier('React'),
|
||||
j.identifier('createClass'),
|
||||
false
|
||||
),
|
||||
[j.objectExpression(properties)]
|
||||
);
|
||||
|
||||
const getComponentName =
|
||||
classPath => classPath.node.id && classPath.node.id.name;
|
||||
|
||||
return {
|
||||
createCreateReactClassCallExpression,
|
||||
findReactES6ClassDeclaration,
|
||||
findReactCreateClass,
|
||||
findReactCreateClassCallExpression,
|
||||
findReactCreateClassModuleExports,
|
||||
findReactCreateClassExportDefault,
|
||||
getComponentName,
|
||||
getReactCreateClassSpec,
|
||||
hasMixins,
|
||||
hasModule,
|
||||
hasReact,
|
||||
isMixinProperty,
|
||||
};
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user