Compare commits
496 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a54333842f | ||
|
|
0c1f2720b3 | ||
|
|
75f7f1e9ba | ||
|
|
09fbf8e0ca | ||
|
|
d17d0d5f50 | ||
|
|
1a7a8486ca | ||
|
|
65548db916 | ||
|
|
894bb03b23 | ||
|
|
94573545f3 | ||
|
|
7734429b89 | ||
|
|
35f092afef | ||
|
|
b837bb7bdd | ||
|
|
c629a0c5ad | ||
|
|
d889322827 | ||
|
|
156dffb961 | ||
|
|
e5befc0a73 | ||
|
|
e3f6a6d916 | ||
|
|
58fecc8cbe | ||
|
|
b5aad9479e | ||
|
|
5db3a0e481 | ||
|
|
0300f2aa22 | ||
|
|
17d36a4cc3 | ||
|
|
c222f57b00 | ||
|
|
1b64508aab | ||
|
|
f2b92d4c7b | ||
|
|
82f82c7543 | ||
|
|
602623661a | ||
|
|
ca3564898d | ||
|
|
5b662b43a0 | ||
|
|
6ba6fc149a | ||
|
|
5a3a39aba4 | ||
|
|
b9b300fcbd | ||
|
|
59f52bce04 | ||
|
|
8d3465060d | ||
|
|
d0af08190e | ||
|
|
b343fcaba3 | ||
|
|
526099c928 | ||
|
|
f0984cf789 | ||
|
|
7be14d8155 | ||
|
|
28f6f034ff | ||
|
|
3be6083ea4 | ||
|
|
f367d0e707 | ||
|
|
9694a0f7ea | ||
|
|
04bfa545f7 | ||
|
|
1971ae8cac | ||
|
|
fd3d16d379 | ||
|
|
dfd406fe4c | ||
|
|
301c571405 | ||
|
|
3376d27915 | ||
|
|
0827646695 | ||
|
|
03578e66b5 | ||
|
|
71e24455a3 | ||
|
|
83a840656c | ||
|
|
ed54fff204 | ||
|
|
5d4f903482 | ||
|
|
946029c921 | ||
|
|
f4321f8624 | ||
|
|
46d05b1191 | ||
|
|
558e8ca312 | ||
|
|
b763d7d029 | ||
|
|
2d61639f90 | ||
|
|
204796868d | ||
|
|
37014e1002 | ||
|
|
c6c4657f83 | ||
|
|
f457394362 | ||
|
|
2b97c608f8 | ||
|
|
15493530f1 | ||
|
|
dd1d49b360 | ||
|
|
e9e8934577 | ||
|
|
2397e35cdd | ||
|
|
0e585d8102 | ||
|
|
6009934176 | ||
|
|
b20c2641d4 | ||
|
|
445a0dac37 | ||
|
|
33abe80b59 | ||
|
|
d7cf1c509b | ||
|
|
7d97f26870 | ||
|
|
b2107ba80b | ||
|
|
d0c431a2a3 | ||
|
|
ab7ef4ed3b | ||
|
|
204edb4a27 | ||
|
|
3c742d50b6 | ||
|
|
1c51cc34cd | ||
|
|
64b9b55a0d | ||
|
|
f9741b0728 | ||
|
|
3085254a91 | ||
|
|
0de35588c1 | ||
|
|
21ea1ac61e | ||
|
|
4fccaa514b | ||
|
|
13ad0c500b | ||
|
|
126a7f5c11 | ||
|
|
bb3bd76fe9 | ||
|
|
4bbdcdb0b8 | ||
|
|
d294a7f30f | ||
|
|
de8d0e35a2 | ||
|
|
faa84b5b85 | ||
|
|
e1e5f17b27 | ||
|
|
0e63000b5c | ||
|
|
151997b1e1 | ||
|
|
a36bcd33c6 | ||
|
|
9194fea915 | ||
|
|
67d9891926 | ||
|
|
05341fb3b3 | ||
|
|
c82afd7e54 | ||
|
|
ed98f2ca57 | ||
|
|
462e450bb3 | ||
|
|
06e5fcc010 | ||
|
|
ac84652e50 | ||
|
|
eee3980749 | ||
|
|
cf83fbe397 | ||
|
|
e221ff7cd4 | ||
|
|
607de16d82 | ||
|
|
d762627312 | ||
|
|
10dab495f2 | ||
|
|
ee1335b6a2 | ||
|
|
8687645c50 | ||
|
|
7b68fcd408 | ||
|
|
92dab0759c | ||
|
|
5c6e59f53c | ||
|
|
067fe27699 | ||
|
|
8db2ba9130 | ||
|
|
1500e9810c | ||
|
|
a3b21b10e4 | ||
|
|
fa03e98426 | ||
|
|
203dba271b | ||
|
|
91562ba934 | ||
|
|
fc5f7e0e85 | ||
|
|
a4123a069e | ||
|
|
a5ddb07cb3 | ||
|
|
917e101c2c | ||
|
|
5c624021ea | ||
|
|
5676a486cf | ||
|
|
1658feade8 | ||
|
|
5f1eceb1ee | ||
|
|
fc3491e0d0 | ||
|
|
9f94244994 | ||
|
|
6ebdd0cfd2 | ||
|
|
858377946f | ||
|
|
826d603b05 | ||
|
|
da4b761c45 | ||
|
|
70a2f8046c | ||
|
|
51bf95f6d1 | ||
|
|
2246f530af | ||
|
|
4e04ef0769 | ||
|
|
b2bbdf8cbf | ||
|
|
9178208ba8 | ||
|
|
7d3db0e5ed | ||
|
|
2869e5b4df | ||
|
|
510146eb6d | ||
|
|
c692d9e844 | ||
|
|
32423a83fc | ||
|
|
d50148591b | ||
|
|
b6451be582 | ||
|
|
5e296d7af8 | ||
|
|
3093a476b1 | ||
|
|
00e56c5155 | ||
|
|
8b9891aa8a | ||
|
|
f7901a2380 | ||
|
|
5c4352b57b | ||
|
|
9ca7c9631a | ||
|
|
d9e99d4688 | ||
|
|
3fd56b4038 | ||
|
|
44659df598 | ||
|
|
0e9ee239a9 | ||
|
|
ce0704a491 | ||
|
|
0acc1d8c78 | ||
|
|
18ef8962f1 | ||
|
|
e748be32da | ||
|
|
0ad14fc038 | ||
|
|
dd61439061 | ||
|
|
40bebf0c86 | ||
|
|
2aa5631e2e | ||
|
|
f39a0f8e40 | ||
|
|
ca19ffb083 | ||
|
|
15272f30f4 | ||
|
|
55176116a2 | ||
|
|
738de8cfa8 | ||
|
|
bd150ec658 | ||
|
|
ee21a604f3 | ||
|
|
43358157cf | ||
|
|
0b65d7555e | ||
|
|
8bc2abd367 | ||
|
|
6556881417 | ||
|
|
c54900f63e | ||
|
|
9c35189ad1 | ||
|
|
e998041229 | ||
|
|
431e1d5608 | ||
|
|
d4c7991aee | ||
|
|
811df48756 | ||
|
|
dbd9d99bcd | ||
|
|
c032743b93 | ||
|
|
418c1fc427 | ||
|
|
fe30279ed0 | ||
|
|
b4f096364f | ||
|
|
9e6a581f68 | ||
|
|
f7e49f3b25 | ||
|
|
2fd5efd92b | ||
|
|
67cf44e7c1 | ||
|
|
9ceaff7318 | ||
|
|
703c825196 | ||
|
|
86adcd6766 | ||
|
|
5a85c5e535 | ||
|
|
cf926338bf | ||
|
|
43930455de | ||
|
|
6f04bd9410 | ||
|
|
59212a538e | ||
|
|
a9b024330c | ||
|
|
d93761af62 | ||
|
|
fb6381fb35 | ||
|
|
1d65f81b16 | ||
|
|
1c40dde782 | ||
|
|
f6c4d2d161 | ||
|
|
b282a0f4f1 | ||
|
|
c1886c6513 | ||
|
|
bd8ecc1caa | ||
|
|
6bbcbc08cf | ||
|
|
f0a4ca5f69 | ||
|
|
de40842597 | ||
|
|
76ec746341 | ||
|
|
14102e8a48 | ||
|
|
4f2d8dfe72 | ||
|
|
7c60bb3e54 | ||
|
|
a62686622b | ||
|
|
a61f4df0b9 | ||
|
|
3266818b42 | ||
|
|
c9ecbaccb3 | ||
|
|
336a0facc1 | ||
|
|
7053f59ad1 | ||
|
|
3373572e15 | ||
|
|
1839bcf109 | ||
|
|
191c0dec32 | ||
|
|
2bc2b52eaa | ||
|
|
36a724feca | ||
|
|
18352090e7 | ||
|
|
4c97ffee34 | ||
|
|
ac5320e887 | ||
|
|
91b10bd37c | ||
|
|
79a2734068 | ||
|
|
32030687ba | ||
|
|
cb01363260 | ||
|
|
0493b27222 | ||
|
|
44d6b94752 | ||
|
|
a2bc7387e4 | ||
|
|
ea5e13893e | ||
|
|
bd044fc919 | ||
|
|
870a29d9b0 | ||
|
|
0e91febb9c | ||
|
|
37ddfa0521 | ||
|
|
5bd449c157 | ||
|
|
b69d7f0d2a | ||
|
|
a0475b3c29 | ||
|
|
3156458041 | ||
|
|
48333acba6 | ||
|
|
4a0456fb8e | ||
|
|
405be0f966 | ||
|
|
000928f9dc | ||
|
|
0f87e8ee87 | ||
|
|
2195a479a8 | ||
|
|
2383fd8813 | ||
|
|
d8b6d260c9 | ||
|
|
6a41ede2d4 | ||
|
|
c04081bc56 | ||
|
|
0b1ecd8872 | ||
|
|
88e90d5601 | ||
|
|
03464dc148 | ||
|
|
1112f1a003 | ||
|
|
c1576fcf97 | ||
|
|
8592eacbf9 | ||
|
|
888cb824d7 | ||
|
|
061527df6c | ||
|
|
96b0a0253f | ||
|
|
c79a59b599 | ||
|
|
fad7d58fc9 | ||
|
|
97efa84676 | ||
|
|
22347ea54d | ||
|
|
46513c6d78 | ||
|
|
1c7d01c2f4 | ||
|
|
7a0f2d71bb | ||
|
|
4104beadbb | ||
|
|
87f4b8be67 | ||
|
|
8d729d7da2 | ||
|
|
f016479289 | ||
|
|
c81cc2e6d5 | ||
|
|
f3aac85d01 | ||
|
|
c6665e3460 | ||
|
|
c7295b9e09 | ||
|
|
9fd9f712bf | ||
|
|
a9c70bcc1c | ||
|
|
80edd6ca87 | ||
|
|
48f46b568d | ||
|
|
279792f891 | ||
|
|
792b69ba11 | ||
|
|
06cff60bc1 | ||
|
|
770ec5946a | ||
|
|
b525a0c061 | ||
|
|
34970fd785 | ||
|
|
ceb5303581 | ||
|
|
f456f8fa8d | ||
|
|
e1fe13d0cb | ||
|
|
01511ea557 | ||
|
|
802241a660 | ||
|
|
e1535fbd71 | ||
|
|
aea8e16b4a | ||
|
|
0d6bb650cb | ||
|
|
ddc4ffffa0 | ||
|
|
d7a5f137ff | ||
|
|
34173638d4 | ||
|
|
c9618587ef | ||
|
|
101bfa3112 | ||
|
|
523bde4dc5 | ||
|
|
483350905b | ||
|
|
5b72334852 | ||
|
|
d13a37ce22 | ||
|
|
0c6f4b3bcc | ||
|
|
0e6fca4a38 | ||
|
|
dfd76be568 | ||
|
|
0dc08c2115 | ||
|
|
6b572b3f25 | ||
|
|
c6c40a5fb3 | ||
|
|
fdc6beed1a | ||
|
|
731aa8ead1 | ||
|
|
8762634cf1 | ||
|
|
735a91c9d5 | ||
|
|
705ce56694 | ||
|
|
94fdf2cf5d | ||
|
|
758c21fb9c | ||
|
|
15360056bd | ||
|
|
932c45a7ab | ||
|
|
e39743f2f8 | ||
|
|
cff4d53a9e | ||
|
|
c2ce1d00cd | ||
|
|
b3e0dc47a8 | ||
|
|
93f979ae18 | ||
|
|
ca3f871646 | ||
|
|
1be6c592a6 | ||
|
|
37cde3d864 | ||
|
|
2afd7186ae | ||
|
|
c19bf9cffe | ||
|
|
7e7579e1ba | ||
|
|
582359aeea | ||
|
|
6c3c643c8e | ||
|
|
3eaed5a122 | ||
|
|
4bb966a7f0 | ||
|
|
0e9e64c550 | ||
|
|
c5998fb483 | ||
|
|
880ada0a1c | ||
|
|
a5e5f53494 | ||
|
|
81f3a5c6cd | ||
|
|
59bee8df21 | ||
|
|
796837b8c7 | ||
|
|
2dc24fc234 | ||
|
|
88923f61a7 | ||
|
|
ba6fea1bf5 | ||
|
|
36d8ce8fab | ||
|
|
153fd9246e | ||
|
|
fac24d462f | ||
|
|
83101b878e | ||
|
|
9d1055b3d2 | ||
|
|
0614d30654 | ||
|
|
9965b6b9dd | ||
|
|
a06de4bc4f | ||
|
|
11a7cb5b73 | ||
|
|
bae6100ae8 | ||
|
|
3ffbb4d096 | ||
|
|
ca5d7bc683 | ||
|
|
1457850b72 | ||
|
|
100af48f53 | ||
|
|
3e211bf662 | ||
|
|
606d6b8fd4 | ||
|
|
b581c8cfc7 | ||
|
|
54d3134da2 | ||
|
|
259392035d | ||
|
|
4b81de93d3 | ||
|
|
93fc188afb | ||
|
|
007b75f78a | ||
|
|
0435216eb6 | ||
|
|
7061d2b25b | ||
|
|
31cdb4c8a7 | ||
|
|
1e76d84569 | ||
|
|
3204135a46 | ||
|
|
a64faf7bf7 | ||
|
|
4a79a718a3 | ||
|
|
e293f998a1 | ||
|
|
dbfaa81ee0 | ||
|
|
6012e94e50 | ||
|
|
0f67f7a782 | ||
|
|
4201ddaf4e | ||
|
|
14f1f8f53a | ||
|
|
61b5bd81d8 | ||
|
|
35306fa7f5 | ||
|
|
b441dcd6f0 | ||
|
|
17d368910f | ||
|
|
321e7e1175 | ||
|
|
bef3dd6760 | ||
|
|
3ded55f9f7 | ||
|
|
962cebf7c5 | ||
|
|
4081678f2e | ||
|
|
b202569c83 | ||
|
|
55a8339781 | ||
|
|
a6707f158b | ||
|
|
96a5fe9e15 | ||
|
|
24f523a351 | ||
|
|
065f8abfe3 | ||
|
|
7b5602d00a | ||
|
|
2bb7e15773 | ||
|
|
cb9cc5de5c | ||
|
|
0244123a52 | ||
|
|
1897bb3d2e | ||
|
|
b353e8a807 | ||
|
|
b3e2aca13a | ||
|
|
e829f8f71f | ||
|
|
9425e58591 | ||
|
|
603c9ef6a8 | ||
|
|
906b8f3f95 | ||
|
|
6cfa71a3c2 | ||
|
|
42f8d155f8 | ||
|
|
009c0b9200 | ||
|
|
c740373b31 | ||
|
|
03f92bb155 | ||
|
|
f8af93237a | ||
|
|
83029eb756 | ||
|
|
99c577e210 | ||
|
|
dd92335dc4 | ||
|
|
8c37499af8 | ||
|
|
a32276e400 | ||
|
|
83b8ad7a31 | ||
|
|
dfdf1f82ae | ||
|
|
cbda00c415 | ||
|
|
21dab3d7d8 | ||
|
|
9b399968eb | ||
|
|
df361e9dd6 | ||
|
|
245f501120 | ||
|
|
1902eafa8d | ||
|
|
7795968382 | ||
|
|
415192c001 | ||
|
|
580e8f0dbb | ||
|
|
095fccb974 | ||
|
|
ebff2bc7a3 | ||
|
|
a78f752143 | ||
|
|
a70d567ec6 | ||
|
|
8d259093bf | ||
|
|
0c6bbf275b | ||
|
|
824a2e0630 | ||
|
|
70a99cd1ee | ||
|
|
60a6665bbd | ||
|
|
2e5dae0c25 | ||
|
|
2d253fe1dc | ||
|
|
bb4788e997 | ||
|
|
510ced1d13 | ||
|
|
767391c26e | ||
|
|
6e805dda24 | ||
|
|
ea82dba555 | ||
|
|
2467c0e651 | ||
|
|
292dd238e7 | ||
|
|
b20a7c2beb | ||
|
|
de2832c0c0 | ||
|
|
d40704ab85 | ||
|
|
2cde6ff60f | ||
|
|
f586c58f96 | ||
|
|
84d4bbb13d | ||
|
|
071201e84b | ||
|
|
15d8200b13 | ||
|
|
d73c2b23e0 | ||
|
|
a808d48169 | ||
|
|
6ed829ff95 | ||
|
|
a52512863e | ||
|
|
4297b1ad55 | ||
|
|
b03f04ff24 | ||
|
|
86eeef1ccd | ||
|
|
ce2d7991c9 | ||
|
|
5d812949a1 | ||
|
|
855c82e224 | ||
|
|
955b472f8b | ||
|
|
c5612b34c9 | ||
|
|
9894e7e1fe | ||
|
|
7dd4576ee4 | ||
|
|
5fc2aad364 | ||
|
|
507ad5bac5 | ||
|
|
cd665be43e | ||
|
|
39c4414d5a | ||
|
|
a203bc5da9 | ||
|
|
36b61d2b11 | ||
|
|
2ce4530d24 | ||
|
|
84a7c2e67c | ||
|
|
c7d2760521 | ||
|
|
875782cc0a | ||
|
|
036e11c9ee | ||
|
|
b4c0661dce | ||
|
|
56dbec46db | ||
|
|
cfe3b75cb0 | ||
|
|
9415d839a5 | ||
|
|
4f7380c4d7 | ||
|
|
fd321cf07d | ||
|
|
12e1bb1daa | ||
|
|
a8866ab824 | ||
|
|
7534bfe2d9 |
4
.gitignore
vendored
@@ -5,15 +5,19 @@ node_modules
|
||||
static
|
||||
.grunt
|
||||
_SpecRunner.html
|
||||
__benchmarks__
|
||||
build/
|
||||
.module-cache
|
||||
*.gem
|
||||
docs/code
|
||||
docs/_site
|
||||
docs/.sass-cache
|
||||
docs/css/react.css
|
||||
docs/js/.module-cache
|
||||
docs/js/JSXTransformer.js
|
||||
docs/js/react.min.js
|
||||
docs/js/docs.js
|
||||
docs/js/jsx-compiler.js
|
||||
docs/js/live_editor.js
|
||||
docs/js/examples
|
||||
docs/downloads
|
||||
|
||||
3
.travis.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
66
CHANGELOG.md
Normal file
@@ -0,0 +1,66 @@
|
||||
## 0.4.0 (July 17, 2013)
|
||||
|
||||
### React
|
||||
|
||||
* Switch from using `id` attribute to `data-reactid` to track DOM nodes. This allows you to integrate with other JS and CSS libraries more easily.
|
||||
* Support for more DOM elements and attributes (e.g., `<canvas>`)
|
||||
* Improved server-side rendering APIs. `React.renderComponentToString(<component>, callback)` allows you to use React on the server and generate markup which can be sent down to the browser.
|
||||
* `prop` improvements: validation and default values. [Read our blog post for details...](http://facebook.github.io/react/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.html)
|
||||
* Support for the `key` prop, which allows for finer control over reconciliation. [Read the docs for details...](http://facebook.github.io/react/docs/multiple-components.html)
|
||||
* Removed `React.autoBind`. [Read our blog post for details...](http://facebook.github.io/react/blog/2013/07/02/react-v0-4-autobind-by-default.html)
|
||||
* Improvements to forms. We've written wrappers around `<input>`, `<textarea>`, `<option>`, and `<select>` in order to standardize many inconsistencies in browser implementations. This includes support for `defaultValue`, and improved implementation of the `onChange` event, and circuit completion. [Read the docs for details...](http://facebook.github.io/react/docs/forms.html)
|
||||
* We've implemented an improved synthetic event system that conforms to the W3C spec.
|
||||
* Updates to your component are batched now, which may result in a significantly faster re-render of components. `this.setState` now takes an optional callback as it's second parameter. If you were using `onClick={this.setState.bind(this, state)}` previously, you'll want to make sure you add a third parameter so that the event is not treated as the callback.
|
||||
|
||||
### JSX
|
||||
|
||||
* Support for comment nodes `<div>{/* this is a comment and won't be rendered */}</div>`
|
||||
* Children are now transformed directly into arguments instead of being wrapped in an array
|
||||
E.g. `<div><Component1/><Component2></div>` is transformed into `React.DOM.div(null, Component1(null), Component2(null))`.
|
||||
Previously this would be transformed into `React.DOM.div(null, [Component1(null), Component2(null)])`.
|
||||
If you were using React without JSX previously, your code should still work.
|
||||
|
||||
### react-tools
|
||||
|
||||
* Fixed a number of bugs when transforming directories
|
||||
* No longer re-write `require()`s to be relative unless specified
|
||||
|
||||
|
||||
## 0.3.3 (June 20, 2013)
|
||||
|
||||
### React
|
||||
|
||||
* Allow reusing the same DOM node to render different components. e.g. `React.renderComponent(<div/>, domNode); React.renderComponent(<span/>, domNode);` will work now.
|
||||
|
||||
### JSX
|
||||
|
||||
* Improved the in-browser transformer so that transformed scripts will execute in the expected scope. The allows components to be defined and used from separate files.
|
||||
|
||||
### react-tools
|
||||
|
||||
* Upgrade Commoner so `require` statements are no longer relativized when passing through the transformer. This was a feature needed when building React, but doesn't translate well for other consumers of `bin/jsx`.
|
||||
* Upgraded our dependencies on Commoner and Recast so they use a different directory for their cache.
|
||||
* Freeze our esprima dependency.
|
||||
|
||||
|
||||
## 0.3.2 (May 31, 2013)
|
||||
|
||||
### JSX
|
||||
|
||||
* Improved compatability with other coding styles (specifically, multiple assignments with a single `var`).
|
||||
|
||||
### react-tools
|
||||
|
||||
* Switch from using the browserified build to shipping individual modules. This allows react-tools to be used with [browserify](https://github.com/substack/node-browserify).
|
||||
|
||||
|
||||
## 0.3.1 (May 30, 2013)
|
||||
|
||||
### react-tools
|
||||
|
||||
* Fix bug in packaging resulting in broken module.
|
||||
|
||||
|
||||
## 0.3.0 (May 29, 2013)
|
||||
|
||||
* Initial public release
|
||||
14
Gruntfile.js
@@ -4,7 +4,9 @@ var exec = require('child_process').exec;
|
||||
var jsxTask = require('./grunt/tasks/jsx');
|
||||
var browserifyTask = require('./grunt/tasks/browserify');
|
||||
var wrapupTask = require('./grunt/tasks/wrapup');
|
||||
var populistTask = require('./grunt/tasks/populist');
|
||||
var phantomTask = require('./grunt/tasks/phantom');
|
||||
var npmTask = require('./grunt/tasks/npm');
|
||||
var releaseTasks = require('./grunt/tasks/release');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
@@ -15,7 +17,9 @@ module.exports = function(grunt) {
|
||||
jsx: require('./grunt/config/jsx/jsx'),
|
||||
browserify: require('./grunt/config/browserify'),
|
||||
wrapup: require('./grunt/config/wrapup'),
|
||||
populist: require('./grunt/config/populist'),
|
||||
phantom: require('./grunt/config/phantom'),
|
||||
npm: require('./grunt/config/npm'),
|
||||
clean: ['./build', './*.gem', './docs/_site', './examples/shared/*.js'],
|
||||
jshint: require('./grunt/config/jshint'),
|
||||
compare_size: require('./grunt/config/compare_size')
|
||||
@@ -42,18 +46,24 @@ module.exports = function(grunt) {
|
||||
// defines global variables instead of using require.
|
||||
grunt.registerMultiTask('wrapup', wrapupTask);
|
||||
|
||||
grunt.registerMultiTask('populist', populistTask);
|
||||
|
||||
grunt.registerMultiTask('phantom', phantomTask);
|
||||
|
||||
grunt.registerMultiTask('npm', npmTask);
|
||||
|
||||
grunt.registerTask('build:basic', ['jsx:debug', 'browserify:basic']);
|
||||
grunt.registerTask('build:transformer', ['jsx:debug', 'browserify:transformer']);
|
||||
grunt.registerTask('build:min', ['jsx:release', 'browserify:min']);
|
||||
grunt.registerTask('build:test', [
|
||||
'jsx:debug',
|
||||
'jsx:jasmine',
|
||||
'jsx:test',
|
||||
'browserify:test'
|
||||
'populist:jasmine',
|
||||
'populist:test'
|
||||
]);
|
||||
|
||||
grunt.registerTask('test', ['build:test', 'phantom:run']);
|
||||
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 jsx:debug once.
|
||||
|
||||
12
README.md
@@ -1,9 +1,9 @@
|
||||
# [React](http://facebook.github.io/react)
|
||||
# [React](http://facebook.github.io/react) [](https://travis-ci.org/facebook/react)
|
||||
|
||||
React is a JavaScript library for building user interfaces.
|
||||
|
||||
* **Declarative:** React uses a declarative paradigm that makes it easier to reason about your application.
|
||||
* **Efficient:** React minimizes interactions with the DOM by using a mock representation of the DOM.
|
||||
* **Efficient:** React computes the minimal set of changes necessary to keep your DOM up-to-date.
|
||||
* **Flexible:** React works with the libraries and frameworks that you already know.
|
||||
|
||||
[Learn how to use React in your own project.](http://facebook.github.io/react/docs/getting-started.html)
|
||||
@@ -36,17 +36,17 @@ The fastest way to get started is to serve JavaScript from the CDN:
|
||||
|
||||
```html
|
||||
<!-- The core React library -->
|
||||
<script src="http://fb.me/react-0.3.0.min.js"></script>
|
||||
<script src="http://fb.me/react-0.3.2.min.js"></script>
|
||||
<!-- In-browser JSX transformer, remove when pre-compiling JSX. -->
|
||||
<script src="http://fb.me/JSXTransformer-0.3.0.js"></script>
|
||||
<script src="http://fb.me/JSXTransformer-0.3.2.js"></script>
|
||||
```
|
||||
|
||||
We've also built a [starter kit](http://facebook.github.io/react/downloads/react-0.3.0.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](http://facebook.github.io/react/downloads/react-0.3.2.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:
|
||||
|
||||
```sh
|
||||
bower install react
|
||||
bower install --save react
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
30
bin/jsx
@@ -3,7 +3,7 @@
|
||||
|
||||
var visitors = require('../vendor/fbtransform/visitors').transformVisitors;
|
||||
var transform = require('../vendor/fbtransform/lib/transform').transform;
|
||||
var debranch = require("../vendor/woodchipper").debranch;
|
||||
var propagate = require("../vendor/constants").propagate;
|
||||
|
||||
require("commoner").resolve(function(id) {
|
||||
var context = this;
|
||||
@@ -25,18 +25,30 @@ require("commoner").resolve(function(id) {
|
||||
|
||||
}).process(function(id, source) {
|
||||
var context = this;
|
||||
var constants = context.config.constants || {};
|
||||
|
||||
// This is where JSX, ES6, etc. desugaring happens.
|
||||
source = transform(visitors.react, source).code;
|
||||
|
||||
return context.makePromise(function(callback) {
|
||||
var constants = context.config.constants || {};
|
||||
// Constant propagation means removing any obviously dead code after
|
||||
// replacing constant expressions with literal (boolean) values.
|
||||
source = propagate(constants, source);
|
||||
|
||||
// Debranching means removing any obviously dead code after
|
||||
// replacing constant conditional expressions with literal
|
||||
// (boolean) values.
|
||||
debranch(constants, source, function(source) {
|
||||
callback(null, source);
|
||||
if (context.config.mocking) {
|
||||
// Make sure there is exactly one newline at the end of the module.
|
||||
source = source.replace(/\s+$/m, "\n");
|
||||
|
||||
return context.getProvidedP().then(function(idToPath) {
|
||||
if (id !== "mock-modules" &&
|
||||
id !== "mocks" &&
|
||||
idToPath.hasOwnProperty("mock-modules")) {
|
||||
return source + '\nrequire("mock-modules").register(' +
|
||||
JSON.stringify(id) + ', module);\n';
|
||||
}
|
||||
|
||||
return source;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return source;
|
||||
});
|
||||
|
||||
@@ -23,8 +23,10 @@ desc "update version to match ../package.json"
|
||||
task :update_version do
|
||||
react_version = JSON.parse(File.read('../package.json'))['version']
|
||||
site_config = YAML.load_file('_config.yml')
|
||||
site_config['react_version'] = react_version
|
||||
File.open('_config.yml', 'w+') { |f| f.write(site_config.to_yaml) }
|
||||
if site_config['react_version'] != react_version
|
||||
site_config['react_version'] = react_version
|
||||
File.open('_config.yml', 'w+') { |f| f.write(site_config.to_yaml) }
|
||||
end
|
||||
end
|
||||
|
||||
desc "build into ../../react-gh-pages"
|
||||
|
||||
@@ -1,14 +1,53 @@
|
||||
---
|
||||
name: React
|
||||
markdown: redcarpet
|
||||
---
|
||||
baseurl: /react
|
||||
react_version: 0.3.0
|
||||
redcarpet:
|
||||
extensions:
|
||||
- fenced_code_blocks
|
||||
pygments: true
|
||||
exclude:
|
||||
url: http://facebook.github.io
|
||||
permalink: /blog/:year/:month/:day/:title.html
|
||||
exclude:
|
||||
- Gemfile
|
||||
- Gemfile.lock
|
||||
- README.md
|
||||
- Rakefile
|
||||
redcarpet:
|
||||
extensions:
|
||||
- fenced_code_blocks
|
||||
pygments: true
|
||||
name: React
|
||||
markdown: redcarpet
|
||||
react_version: 0.4.0
|
||||
description: A JavaScript library for building user interfaces
|
||||
relative_permalinks: true
|
||||
nav_docs_sections:
|
||||
- title: Quick Start
|
||||
items:
|
||||
- id: getting-started
|
||||
title: Getting Started
|
||||
- id: tutorial
|
||||
title: Tutorial
|
||||
- title: Guides
|
||||
items:
|
||||
- id: why-react
|
||||
title: Why React?
|
||||
- id: displaying-data
|
||||
title: Displaying Data
|
||||
subitems:
|
||||
- id: jsx-in-depth
|
||||
title: JSX in Depth
|
||||
- id: jsx-gotchas
|
||||
title: JSX Gotchas
|
||||
- id: interactivity-and-dynamic-uis
|
||||
title: Interactivity and Dynamic UIs
|
||||
- id: multiple-components
|
||||
title: Multiple Components
|
||||
- id: reusable-components
|
||||
title: Reusable Components
|
||||
- id: forms
|
||||
title: Forms
|
||||
- id: working-with-the-browser
|
||||
title: Working With the Browser
|
||||
subitems:
|
||||
- id: more-about-refs
|
||||
title: More About Refs
|
||||
- id: tooling-integration
|
||||
title: Tooling integration
|
||||
- id: reference
|
||||
title: Reference
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
@import '_typography';
|
||||
@import '_solarized';
|
||||
|
||||
@mixin code-typography {
|
||||
font-family: 'source-code-pro', Menlo, 'Courier New', Consolas, monospace;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
$skinnyContentWidth: 650px;
|
||||
$contentWidth: 920px;
|
||||
$contentPadding: 20px;
|
||||
@@ -200,6 +206,9 @@ li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
ul ul {
|
||||
margin-left: 20px;
|
||||
}
|
||||
li {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -350,6 +359,22 @@ section.black content {
|
||||
padding-bottom: 18px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blog
|
||||
*/
|
||||
|
||||
.blogContent {
|
||||
padding-top: 20px;
|
||||
|
||||
blockquote {
|
||||
padding: 5px 15px;
|
||||
margin: 20px 0;
|
||||
background-color: #f8f5ec;
|
||||
border-left: 5px solid #f7ebc6;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Docs
|
||||
*/
|
||||
@@ -396,6 +421,26 @@ section.black content {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
/* JSX Compiler */
|
||||
|
||||
.jsxCompiler {
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
width: 1220px;
|
||||
|
||||
#jsxCompiler {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.playgroundPreview {
|
||||
padding: 14px;
|
||||
width: 600px;
|
||||
|
||||
pre {
|
||||
@include code-typography;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Button */
|
||||
|
||||
@@ -469,6 +514,10 @@ p {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
figure {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inner-content {
|
||||
float: right;
|
||||
width: $skinnyContentWidth;
|
||||
@@ -479,12 +528,16 @@ p {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Blog */
|
||||
|
||||
.post-list-item + .post-list-item {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
/* Code Mirror */
|
||||
|
||||
div.CodeMirror pre, div.CodeMirror-linenumber, code {
|
||||
font-family: 'source-code-pro', Menlo, 'Courier New', Consolas, monospace;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
@include code-typography;
|
||||
}
|
||||
|
||||
div.CodeMirror-linenumber:after {
|
||||
@@ -628,3 +681,7 @@ p code {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.post {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
10
docs/_includes/blog_post.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<h1><a href="/react{{ page.url }}">{{ page.title }}</a></h1>
|
||||
<p class="meta">{{ page.date | date_to_string }} by {{ page.author }}</p>
|
||||
|
||||
<div id="post">
|
||||
{% if content != '' %}
|
||||
{{ page.excerpt }}
|
||||
{% else %}
|
||||
{{ page.content }}
|
||||
{% endif %}
|
||||
</div>
|
||||
10
docs/_includes/nav_blog.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="nav-docs">
|
||||
<div class="nav-docs-section">
|
||||
<h3>Recent posts</h3>
|
||||
<ul>
|
||||
{% for post in site.posts %}
|
||||
<li><a href="/react{{ post.url }}"{% if page.title == post.title %} class="active"{% endif %}>{{ post.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,23 +1,27 @@
|
||||
<div class="nav-docs">
|
||||
<div class="nav-docs-section">
|
||||
<h3>Getting started</h3>
|
||||
<ul>
|
||||
<li><a href="/react/docs/getting-started.html"{% if page.id == 'docs-getting-started' %} class="active"{% endif %}>Getting Started</a></li>
|
||||
<li><a href="/react/docs/tutorial.html"{% if page.id == 'docs-tutorial' %} class="active"{% endif %}>Tutorial</a></li>
|
||||
<li><a href="/react/docs/common-questions.html"{% if page.id == 'docs-common-questions' %} class="active"{% endif %}>Common Questions</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="nav-docs-section">
|
||||
<h3>Reference</h3>
|
||||
<ul>
|
||||
<li><a href="/react/docs/syntax.html"{% if page.id == 'docs-syntax' %} class="active"{% endif %}>JSX Syntax</a></li>
|
||||
<li><a href="/react/docs/component-basics.html"{% if page.id == 'docs-component-basics' %} class="active"{% endif %}>Component Basics</a></li>
|
||||
<li><a href="/react/docs/component-data.html"{% if page.id == 'docs-component-data' %} class="active"{% endif %}>Component Data</a></li>
|
||||
<li><a href="/react/docs/component-lifecycle.html"{% if page.id == 'docs-component-lifecycle' %} class="active"{% endif %}>Component Lifecycle</a></li>
|
||||
<li><a href="/react/docs/event-handling.html"{% if page.id == 'docs-event-handling' %} class="active"{% endif %}>Event Handling</a></li>
|
||||
<li><a href="/react/docs/advanced-components.html"{% if page.id == 'docs-advanced-components' %} class="active"{% endif %}>Advanced Components</a></li>
|
||||
<li><a href="/react/docs/api.html"{% if page.id == 'docs-api' %} class="active"{% endif %}>API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% for section in site.nav_docs_sections %}
|
||||
<div class="nav-docs-section">
|
||||
<h3>{{ section.title }}</h3>
|
||||
<ul>
|
||||
{% for item in section.items %}
|
||||
<li>
|
||||
<a href="/react/docs/{{ item.id }}.html"{% if page.id == item.id %} class="active"{% endif %}>
|
||||
{{ item.title }}
|
||||
</a>
|
||||
{% if item.subitems %}
|
||||
<ul>
|
||||
{% for subitem in item.subitems %}
|
||||
<li>
|
||||
<a href="/react/docs/{{ subitem.id }}.html"{% if page.id == subitem.id %} class="active"{% endif %}>
|
||||
{{ subitem.title }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -11,14 +11,14 @@ var MarkdownEditor = React.createClass({\n\
|
||||
getInitialState: function() {\n\
|
||||
return {value: 'Type some *markdown* here!'};\n\
|
||||
},\n\
|
||||
handleKeyUp: React.autoBind(function() {\n\
|
||||
handleInput: function() {\n\
|
||||
this.setState({value: this.refs.textarea.getDOMNode().value});\n\
|
||||
}),\n\
|
||||
},\n\
|
||||
render: function() {\n\
|
||||
return (\n\
|
||||
<div className=\"MarkdownEditor\">\n\
|
||||
<h3>Input</h3>\n\
|
||||
<textarea onKeyUp={this.handleKeyUp} ref=\"textarea\">\n\
|
||||
<textarea onInput={this.handleInput} ref=\"textarea\">\n\
|
||||
{this.state.value}\n\
|
||||
</textarea>\n\
|
||||
<h3>Output</h3>\n\
|
||||
|
||||
@@ -3,27 +3,24 @@
|
||||
*/
|
||||
|
||||
var TIMER_COMPONENT = "\
|
||||
/** @jsx React.DOM */\n\
|
||||
var Timer = React.createClass({\n\
|
||||
getInitialState: function() {\n\
|
||||
return {secondsElapsed: 0};\n\
|
||||
},\n\
|
||||
tick: React.autoBind(function() {\n\
|
||||
tick: function() {\n\
|
||||
this.setState({secondsElapsed: this.state.secondsElapsed + 1});\n\
|
||||
}),\n\
|
||||
},\n\
|
||||
componentDidMount: function() {\n\
|
||||
setInterval(this.tick, 1000);\n\
|
||||
},\n\
|
||||
render: function() {\n\
|
||||
return (\n\
|
||||
<div>\n\
|
||||
{'Seconds Elapsed: ' + this.state.secondsElapsed}\n\
|
||||
</div>\n\
|
||||
return React.DOM.div({},\n\
|
||||
'Seconds Elapsed: ' + this.state.secondsElapsed\n\
|
||||
);\n\
|
||||
}\n\
|
||||
});\n\
|
||||
\n\
|
||||
React.renderComponent(<Timer />, mountNode);\
|
||||
React.renderComponent(Timer({}), mountNode);\
|
||||
";
|
||||
|
||||
React.renderComponent(
|
||||
|
||||
@@ -6,48 +6,38 @@ var TODO_COMPONENT = "\
|
||||
/** @jsx React.DOM */\n\
|
||||
var TodoList = React.createClass({\n\
|
||||
render: function() {\n\
|
||||
var items = this.props.items.map(function(item) {\n\
|
||||
return <li>{item}</li>;\n\
|
||||
});\n\
|
||||
return <ul>{items}</ul>;\n\
|
||||
var createItem = function(itemText) {\n\
|
||||
return <li>{itemText}</li>;\n\
|
||||
};\n\
|
||||
return <ul>{this.props.items.map(createItem)}</ul>;\n\
|
||||
}\n\
|
||||
});\n\
|
||||
\n\
|
||||
var TodoCreate = React.createClass({\n\
|
||||
handleSubmit: function() {\n\
|
||||
var textInput = this.refs.textInput.getDOMNode();\n\
|
||||
this.props.onCreate(textInput.value);\n\
|
||||
textInput.value = '';\n\
|
||||
return false;\n\
|
||||
},\n\
|
||||
render: function() {\n\
|
||||
return (\n\
|
||||
<form onSubmit={this.handleSubmit.bind(this)}>\n\
|
||||
<input type=\"text\" ref=\"textInput\" />\n\
|
||||
<button>Add</button>\n\
|
||||
</form>\n\
|
||||
);\n\
|
||||
}\n\
|
||||
});\n\
|
||||
\n\
|
||||
var TodoApp = React.createClass({\n\
|
||||
getInitialState: function() {\n\
|
||||
return {items: []};\n\
|
||||
return {items: [], text: ''};\n\
|
||||
},\n\
|
||||
onItemCreate: function(value) {\n\
|
||||
this.setState({items: this.state.items.concat([value])});\n\
|
||||
onKey: function(e) {\n\
|
||||
this.setState({text: e.target.value});\n\
|
||||
},\n\
|
||||
handleSubmit: function(e) {\n\
|
||||
e.preventDefault();\n\
|
||||
var nextItems = this.state.items.concat([this.state.text]);\n\
|
||||
var nextText = '';\n\
|
||||
this.setState({items: nextItems, text: nextText});\n\
|
||||
},\n\
|
||||
render: function() {\n\
|
||||
return (\n\
|
||||
<div>\n\
|
||||
<h3>TODO</h3>\n\
|
||||
<TodoList items={this.state.items} />\n\
|
||||
<TodoCreate onCreate={this.onItemCreate.bind(this)} />\n\
|
||||
<form onSubmit={this.handleSubmit.bind(this)}>\n\
|
||||
<input onKeyUp={this.onKey.bind(this)} value={this.state.text} />\n\
|
||||
<button>{'Add #' + (this.state.items.length + 1)}</button>\n\
|
||||
</form>\n\
|
||||
</div>\n\
|
||||
);\n\
|
||||
}\n\
|
||||
});\n\
|
||||
\n\
|
||||
React.renderComponent(<TodoApp />, mountNode);\
|
||||
";
|
||||
|
||||
|
||||
19
docs/_js/jsx-compiler.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @jsx React.DOM
|
||||
*/
|
||||
|
||||
var HELLO_COMPONENT = "\
|
||||
/** @jsx React.DOM */\n\
|
||||
var HelloMessage = React.createClass({\n\
|
||||
render: function() {\n\
|
||||
return <div>{'Hello ' + this.props.name}</div>;\n\
|
||||
}\n\
|
||||
});\n\
|
||||
\n\
|
||||
React.renderComponent(<HelloMessage name=\"John\" />, mountNode);\
|
||||
";
|
||||
|
||||
React.renderComponent(
|
||||
<ReactPlayground codeText={HELLO_COMPONENT} renderCode={true} />,
|
||||
document.getElementById('jsxCompiler')
|
||||
);
|
||||
@@ -81,7 +81,7 @@ var ReactPlayground = React.createClass({
|
||||
/>;
|
||||
} else if (this.state.mode === this.MODES.JS) {
|
||||
content =
|
||||
<div class={{playgroundJS: true, playgroundStage: true}}>
|
||||
<div class="playgroundJS playgroundStage">
|
||||
{this.getDesugaredCode()}
|
||||
</div>;
|
||||
}
|
||||
@@ -111,9 +111,19 @@ var ReactPlayground = React.createClass({
|
||||
} catch (e) { }
|
||||
|
||||
try {
|
||||
eval(this.getDesugaredCode());
|
||||
if (this.props.renderCode) {
|
||||
React.renderComponent(
|
||||
<pre>{this.getDesugaredCode()}</pre>,
|
||||
mountNode
|
||||
);
|
||||
} else {
|
||||
eval(this.getDesugaredCode());
|
||||
}
|
||||
} catch (e) {
|
||||
React.renderComponent(<div content={e.toString()} class={{playgroundError: true}} />, mountNode);
|
||||
React.renderComponent(
|
||||
<div content={e.toString()} class="playgroundError" />,
|
||||
mountNode
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<meta property="og:description" content="A JavaScript library for building user interfaces" />
|
||||
|
||||
<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="/react/css/react.css">
|
||||
<link rel="stylesheet" href="/react/css/syntax.css">
|
||||
@@ -34,13 +35,14 @@
|
||||
<div class="nav-main">
|
||||
<div class="wrap">
|
||||
<a class="nav-home" href="/react/index.html">
|
||||
<img class="nav-logo" alt="" src="/react/img/logo_small.png">
|
||||
<img class="nav-logo" alt="" src="/react/img/logo_small.png" width="38" height="38">
|
||||
React
|
||||
</a>
|
||||
<ul class="nav-site">
|
||||
<li><a href="/react/docs/getting-started.html"{% if page.sectionid == 'docs' %} class="active"{% endif %}>docs</a></li>
|
||||
<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><a href="http://github.com/facebook/react">github</a>
|
||||
</ul>
|
||||
<!-- <iframe src="http://ghbtns.com/github-btn.html?user=facebook&repo=react.js&type=fork"allowtransparency="true" frameborder="0" scrolling="0" width="62" height="20"></iframe> -->
|
||||
@@ -70,6 +72,7 @@
|
||||
<div class="right">© 2013 Facebook Inc.</div>
|
||||
</footer>
|
||||
</div>
|
||||
<div id="fb-root"></div>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
@@ -77,7 +80,16 @@
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-41298772-1', 'facebook.github.io');
|
||||
ga('send', 'pageview');
|
||||
|
||||
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
|
||||
|
||||
(function(d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) return;
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=623268441017527";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -19,5 +19,7 @@ sectionid: docs
|
||||
<a class="docs-next" href="/react/docs/{{ page.next }}">Next →</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="fb-comments" data-width="650" data-num-posts="10" data-href="{{ site.url }}{{ site.baseurl }}{{ page.url }}"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
---
|
||||
layout: default
|
||||
sectionid: blog
|
||||
---
|
||||
|
||||
<section class="content wrap">
|
||||
<h2>{{ page.title }}</h2>
|
||||
<p class="meta">{{ page.date | date_to_string }}</p>
|
||||
<section class="content wrap blogContent">
|
||||
{% include nav_blog.html %}
|
||||
<div class="inner-content">
|
||||
<h1>{{ page.title }}</h1>
|
||||
<p class="meta">{{ page.date | date: "%B %e, %Y" }} by {{ page.author }}</p>
|
||||
|
||||
<div id="post">
|
||||
{{ content }}
|
||||
<hr>
|
||||
|
||||
<div class="post">
|
||||
{{ content }}
|
||||
</div>
|
||||
|
||||
<div class="fb-like" data-send="true" data-width="650" data-show-faces="false"></div>
|
||||
<div class="fb-comments" data-width="650" data-num-posts="10" data-href="{{ site.url }}{{ site.baseurl }}{{ page.url }}"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
11
docs/_posts/2013-06-02-jsfiddle-integration.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: JSFiddle Integration
|
||||
layout: post
|
||||
author: Christopher Chedeau
|
||||
---
|
||||
|
||||
[JSFiddle](http://jsfiddle.net) just announced support for React. This is an exciting news as it makes collaboration on snippets of code a lot easier. You can play around this **[base React JSFiddle](http://jsfiddle.net/vjeux/kb3gN/)**, fork it and share it! A [fiddle without JSX](http://jsfiddle.net/vjeux/VkebS/) is also available.
|
||||
|
||||
|
||||
<blockquote class="twitter-tweet" align="center"><p>React (by Facebook) is now available on JSFiddle. <a href="http://t.co/wNQf9JPv5u" title="http://facebook.github.io/react/">facebook.github.io/react/</a></p>— JSFiddle (@jsfiddle) <a href="https://twitter.com/jsfiddle/status/341114115781177344">June 2, 2013</a></blockquote>
|
||||
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
91
docs/_posts/2013-06-05-why-react.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Why did we build React?
|
||||
layout: post
|
||||
author: Pete Hunt
|
||||
---
|
||||
|
||||
There are a lot of JavaScript MVC frameworks out there. Why did we build React
|
||||
and why would you want to use it?
|
||||
|
||||
## React isn't an MVC framework.
|
||||
|
||||
React is a library for building composable user interfaces. It encourages
|
||||
the creation of reusable UI components which present data that changes over
|
||||
time.
|
||||
|
||||
## React doesn't use templates.
|
||||
|
||||
Traditionally, web application UIs are built using templates or HTML directives.
|
||||
These templates dictate the full set of abstractions that you are allowed to use
|
||||
to build your UI.
|
||||
|
||||
React approaches building user interfaces differently by breaking them into
|
||||
**components**. This means React uses JavaScript to generate markup, which we
|
||||
see as an advantage over templates for a few reasons:
|
||||
|
||||
- **JavaScript is a flexible, powerful programming language** with the ability
|
||||
to build abstractions. This is incredibly important in large applications.
|
||||
- By unifying your markup with its corresponding view logic, React can actually
|
||||
make views **easier to extend and maintain**.
|
||||
- By baking an understanding of markup and content into JavaScript, there's
|
||||
**no manual string concatenation** and therefore less surface area for XSS
|
||||
vulnerabilities.
|
||||
|
||||
We've also created [JSX](http://facebook.github.io/react/docs/syntax.html), an optional
|
||||
syntax extension, in case you prefer the readability of HTML to raw JavaScript.
|
||||
|
||||
## Reactive updates are dead simple.
|
||||
|
||||
React really shines when your data changes over time.
|
||||
|
||||
In a traditional JavaScript application, you need to look at what data changed
|
||||
and imperatively make changes to the DOM to keep it up-to-date. Even AngularJS,
|
||||
which provides a declarative interface via directives and data binding [requires
|
||||
a linking function to manually update DOM nodes](http://docs.angularjs.org/guide/directive#reasonsbehindthecompilelinkseparation).
|
||||
|
||||
React takes a different approach.
|
||||
|
||||
When your component is first initialized, the `render` method is called,
|
||||
generating a lightweight representation of your view. From that representation,
|
||||
a string of markup is produced, and injected into the document. When your data
|
||||
changes, the `render` method is called again. In order to perform updates as
|
||||
efficiently as possible, we diff the return value from the previous call to
|
||||
`render` with the new one, and generate a minimal set of changes to be applied
|
||||
to the DOM.
|
||||
|
||||
> The data returned from `render` is neither a string nor a DOM node -- it's a
|
||||
> lightweight description of what the DOM should look like.
|
||||
|
||||
We call this process **reconciliation**. Check out
|
||||
[this jsFiddle](http://jsfiddle.net/fv6RD/3/) to see an example of
|
||||
reconciliation in action.
|
||||
|
||||
Because this re-render is so fast (around 1ms for TodoMVC), the developer
|
||||
doesn't need to explicitly specify data bindings. We've found this approach
|
||||
makes it easier to build apps.
|
||||
|
||||
## HTML is just the beginning.
|
||||
|
||||
Because React has its own lightweight representation of the document, we can do
|
||||
some pretty cool things with it:
|
||||
|
||||
- Facebook has dynamic charts that render to `<canvas>` instead of HTML.
|
||||
- Instagram is a "single page" web app built entirely with React and
|
||||
`Backbone.Router`. Designers regularly contribute React code with JSX.
|
||||
- We've built internal prototypes that run React apps in a web worker and use
|
||||
React to drive **native iOS views** via an Objective-C bridge.
|
||||
- You can run React
|
||||
[on the server](http://github.com/petehunt/react-server-rendering)
|
||||
for SEO, performance, code sharing and overall flexibility.
|
||||
- Events behave in a consistent, standards-compliant way in all browsers
|
||||
(including IE8) and automatically use
|
||||
[event delegation](http://davidwalsh.name/event-delegate).
|
||||
|
||||
Head on over to
|
||||
[facebook.github.io/react](http://facebook.github.io/react) to check
|
||||
out what we have built. Our documentation is geared towards building
|
||||
apps with the framework, but if you are interested in the
|
||||
nuts and bolts
|
||||
[get in touch](http://facebook.github.io/react/support.html) with us!
|
||||
|
||||
Thanks for reading!
|
||||
48
docs/_posts/2013-06-12-community-roundup.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: "Community Round-up #1"
|
||||
layout: post
|
||||
author: Vjeux
|
||||
---
|
||||
|
||||
React was open sourced two weeks ago and it's time for a little round-up of what has been going on.
|
||||
|
||||
## Khan Academy Question Editor
|
||||
|
||||
It looks like [Ben Alpert](http://benalpert.com/) is the first person outside of Facebook and Instagram to push React code to production. We are very grateful for his contributions in form of pull requests, bug reports and presence on IRC ([#reactjs on Freenode](irc://chat.freenode.net/reactjs)). Ben wrote about his experience using React:
|
||||
|
||||
> I just rewrote a 2000-line project in React and have now made a handful of pull requests to React. Everything about React I've seen so far seems really well thought-out and I'm proud to be the first non-FB/IG production user of React.
|
||||
>
|
||||
> The project that I rewrote in React (and am continuing to improve) is the Khan Academy question editor which content creators can use to enter questions and hints that will be presented to students:
|
||||
> <figure>[](http://benalpert.com/2013/06/09/using-react-to-speed-up-khan-academy.html)</figure>
|
||||
>
|
||||
> [Read the full post...](http://benalpert.com/2013/06/09/using-react-to-speed-up-khan-academy.html)
|
||||
|
||||
## Pimp my Backbone.View (by replacing it with React)
|
||||
|
||||
[Paul Seiffert](https://blog.mayflower.de/) wrote a blog post that explains how to integrate React into Backbone applications.
|
||||
|
||||
> React has some interesting concepts for JavaScript view objects that can be used to eliminate this one big problem I have with Backbone.js.
|
||||
>
|
||||
> As in most MVC implementations (although React is probably just a VC implementation), a view is one portion of the screen that is managed by a controlling object. This object is responsible for deciding when to re-render the view and how to react to user input. With React, these view-controllers objects are called components. A component knows how to render its view and how to handle to the user's interaction with it.
|
||||
>
|
||||
> The interesting thing is that React is figuring out by itself when to re-render a view and how to do this in the most efficient way.
|
||||
>
|
||||
> [Read the full post...](https://blog.mayflower.de/3937-Backbone-React.html)
|
||||
|
||||
## Using facebook's React with require.js
|
||||
|
||||
[Mario Mueller](http://blog.xenji.com/) wrote a menu component in React and was able to easily integrate it with require.js, EventEmitter2 and bower.
|
||||
|
||||
> I recently stumbled upon facebook's React library, which is a Javascript library for building reusable frontend components. Even if this lib is only at version 0.3.x it behaves very stable, it is fast and is fun to code. I'm a big fan of require.js, so I tried to use React within the require.js eco system. It was not as hard as expected and here are some examples and some thoughts about it.
|
||||
>
|
||||
> [Read the full post...](http://blog.xenji.com/2013/06/facebooks-react-require-js.html)
|
||||
|
||||
## Origins of React
|
||||
|
||||
[Pete Hunt](http://www.petehunt.net/blog/) explained what differentiates React from other JavaScript libraries in [a previous blog post](http://facebook.github.io/react/blog/2013/06/05/why-react.html). [Lee Byron](http://leebyron.com/) gives another perspective on Quora:
|
||||
|
||||
> React isn't quite like any other popular Javascript libraries, and it solves a very specific problem: complex UI rendering. It's also intended to be used along side many other popular libraries. For example, React works well with Backbone.js, amongst many others.
|
||||
>
|
||||
> React was born out of frustrations with the common pattern of writing two-way data bindings in complex MVC apps. React is an implementation of one-way data bindings.
|
||||
>
|
||||
> [Read the full post...](http://www.quora.com/React-JS-Library/How-is-Facebooks-React-JavaScript-library/answer/Lee-Byron?srid=3DcX)
|
||||
71
docs/_posts/2013-06-19-community-roundup-2.md
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: "Community Round-up #2"
|
||||
layout: post
|
||||
author: Vjeux
|
||||
---
|
||||
|
||||
Since the launch we have received a lot of feedback and are actively working on React 0.4. In the meantime, here are the highlights of this week.
|
||||
|
||||
## Some quick thoughts on React
|
||||
|
||||
[Andrew Greig](http://www.andrewgreig.com/) made a blog post that gives a high level description of what React is.
|
||||
|
||||
> I have been using Facebooks recently released Javascript framework called React.js for the last few days and have managed to obtain a rather high level understanding of how it works and formed a good perspective on how it fits in to the entire javascript framework ecosystem.
|
||||
>
|
||||
> Basically, React is not an MVC framework. It is not a replacement for Backbone or Knockout or Angular, instead it is designed to work with existing frameworks and help extend their functionality.
|
||||
>
|
||||
> It is designed for building big UIs. The type where you have lots of reusable components that are handling events and presenting and changing some backend data. In a traditional MVC app, React fulfils the role of the View. So you would still need to handle the Model and Controller on your own.
|
||||
>
|
||||
> I found the best way to utilise React was to pair it with Backbone, with React replacing the Backbone View, or to write your own Model/Data object and have React communicate with that.
|
||||
>
|
||||
> [Read the full post...](http://www.andrewgreig.com/637/)
|
||||
|
||||
## React and Socket.IO Chat Application
|
||||
|
||||
[Danial Khosravi](http://danialk.github.io/) made a real-time chat application that interacts with the back-end using Socket.IO.
|
||||
|
||||
> A week ago I was playing with AngularJS and [this little chat application](https://github.com/btford/angular-socket-io-im) which uses socket.io and nodejs for realtime communication. Yesterday I saw a post about ReactJS in [EchoJS](http://www.echojs.com/) and started playing with this UI library. After playing a bit with React, I decided to write and chat application using React and I used Bran Ford's Backend for server side of this little app.
|
||||
> <figure>[](http://danialk.github.io/blog/2013/06/16/reactjs-and-socket-dot-io-chat-application/)</figure>
|
||||
>
|
||||
> [Read the full post...](http://danialk.github.io/blog/2013/06/16/reactjs-and-socket-dot-io-chat-application/)
|
||||
|
||||
## React and Other Frameworks
|
||||
|
||||
[Pete Hunt](http://www.petehunt.net/blog/) wrote an answer on Quora comparing React and Angular directives. At the end, he explains how you can make an Angular directive that is in fact being rendered with React.
|
||||
|
||||
> To set the record straight: React components are far more powerful than Angular templates; they should be compared with Angular's directives instead. So I took the first Google hit for "AngularJS directive tutorial" (AngularJS Directives Tutorial - Fundoo Solutions), rewrote it in React and compared them. [...]
|
||||
>
|
||||
> We've designed React from the beginning to work well with other libraries. Angular is no exception. Let's take the original Angular example and use React to implement the fundoo-rating directive.
|
||||
>
|
||||
> [Read the full post...](http://www.quora.com/Pete-Hunt/Posts/Facebooks-React-vs-AngularJS-A-Closer-Look)
|
||||
|
||||
In the same vein, [Markov Twain](https://twitter.com/markov_twain/status/345702941845499906) re-implemented the examples on the front-page [with Ember](http://jsbin.com/azihiw/2/edit) and [Vlad Yazhbin](https://twitter.com/vla) re-implemented the tutorial [with Angular](http://jsfiddle.net/vla/Cdrse/).
|
||||
|
||||
## Web Components: React & x-tags
|
||||
|
||||
Mozilla and Google are actively working on Web Components. [Vjeux](http://blog.vjeux.com/) wrote a proof of concept that shows how to implement them using React.
|
||||
|
||||
> Using [x-tags](http://www.x-tags.org/) from Mozilla, we can write custom tags within the DOM. This is a great opportunity to be able to write reusable components without being tied to a particular library. I wrote [x-react](https://github.com/vjeux/react-xtags/) to have them being rendered in React.
|
||||
> <figure>[](http://blog.vjeux.com/2013/javascript/custom-components-react-x-tags.html)</figure>
|
||||
>
|
||||
> [Read the full post...](http://blog.vjeux.com/2013/javascript/custom-components-react-x-tags.html)
|
||||
|
||||
## React TodoMVC Example
|
||||
|
||||
[TodoMVC.com](http://todomvc.com/) is a website that collects various implementations of the same basic Todo app. [Pete Hunt](http://www.petehunt.net/blog/) wrote an idiomatic React version.
|
||||
|
||||
> Developers these days are spoiled with choice when it comes to selecting an MV* framework for structuring and organizing their JavaScript web apps.
|
||||
>
|
||||
> To help solve this problem, we created TodoMVC - a project which offers the same Todo application implemented using MV* concepts in most of the popular JavaScript MV* frameworks of today.
|
||||
> <figure>[](http://todomvc.com/labs/architecture-examples/react/)</figure>
|
||||
>
|
||||
> [Read the source code...](https://github.com/tastejs/todomvc/tree/gh-pages/labs/architecture-examples/react)
|
||||
|
||||
## JSX is not HTML
|
||||
|
||||
Many of you pointed out differences between JSX and HTML. In order to clear up some confusion, we have added some documentation that covers the four main differences:
|
||||
|
||||
- [Whitespace removal](http://facebook.github.io/react/docs/jsx-is-not-html.html)
|
||||
- [HTML Entities](http://facebook.github.io/react/docs/jsx-is-not-html.html)
|
||||
- [Comments](http://facebook.github.io/react/docs/jsx-is-not-html.html)
|
||||
- [Custom HTML Attributes](http://facebook.github.io/react/docs/jsx-is-not-html.html)
|
||||
24
docs/_posts/2013-06-21-react-v0-3-3.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: "React v0.3.3"
|
||||
layout: post
|
||||
author: Paul O'Shannessy
|
||||
---
|
||||
|
||||
We have a ton of great stuff coming in v0.4, but in the meantime we're releasing v0.3.3. This release addresses some small issues people were having and simplifies our tools to make them easier to use.
|
||||
|
||||
|
||||
## react-tools
|
||||
|
||||
* Upgrade Commoner so `require` statements are no longer relativized when passing through the transformer. This was a feature needed when building React, but doesn't translate well for other consumers of `bin/jsx`.
|
||||
* Upgraded our dependencies on Commoner and Recast so they use a different directory for their cache.
|
||||
* Freeze our esprima dependency.
|
||||
|
||||
|
||||
## React
|
||||
|
||||
* Allow reusing the same DOM node to render different components. e.g. `React.renderComponent(<div/>, domNode); React.renderComponent(<span/>, domNode);` will work now.
|
||||
|
||||
|
||||
## JSXTransformer
|
||||
|
||||
* Improved the in-browser transformer so that transformed scripts will execute in the expected scope. The allows components to be defined and used from separate files.
|
||||
91
docs/_posts/2013-06-27-community-roundup-3.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: "Community Round-up #3"
|
||||
layout: post
|
||||
author: Vjeux
|
||||
---
|
||||
|
||||
The highlight of this week is that an interaction-heavy app has been ported to React. React components are solving issues they had with nested views.
|
||||
|
||||
## Moving From Backbone To React
|
||||
|
||||
[Clay Allsopp](http://twitter.com/clayallsopp) successfuly ported [Propeller](http://usepropeller.com/blog/posts/from-backbone-to-react/), a fairly big, interaction-heavy JavaScript app, to React.
|
||||
|
||||
> [<img style="float: right; margin: 0 0 10px 10px;" src="/react/img/blog/propeller-logo.png" />](http://usepropeller.com/blog/posts/from-backbone-to-react/)Subviews involve a lot of easy-to-forget boilerplate that Backbone (by design) doesn't automate. Libraries like Backbone.Marionette offer more abstractions to make view nesting easier, but they're all limited by the fact that Backbone delegates how and went view-document attachment occurs to the application code.
|
||||
>
|
||||
> React, on the other hand, manages the DOM and only exposes real nodes at select points in its API. The "elements" you code in React are actually objects which wrap DOM nodes, not the actual objects which get inserted into the DOM. Internally, React converts those abstractions into actual DOMElements and fills out the document accordingly. [...]
|
||||
>
|
||||
> We moved about 20 different Backbone view classes to React over the past few weeks, including the live-preview pane that you see in our little iOS demo. Most importantly, it's allowed us to put energy into making each component work great on its own, instead of spending extra cycles to ensure they function in unison. For that reason, we think React is a more scalable way to build view-intensive apps than Backbone alone, and it doesn't require you to drop-everything-and-refactor like a move to Ember or Angular would demand.
|
||||
>
|
||||
> [Read the full post...](http://usepropeller.com/blog/posts/from-backbone-to-react/)
|
||||
|
||||
## Grunt Task for JSX
|
||||
|
||||
[Eric Clemmons](http://ericclemmons.github.io/) wrote a task for [Grunt](http://gruntjs.com/) that applies the JSX transformation to your Javascript files. It also works with [Browserify](http://browserify.org/) if you want all your files to be concatenated and minified together.
|
||||
|
||||
> Grunt task for compiling Facebook React's .jsx templates into .js
|
||||
>
|
||||
> ```javascript
|
||||
grunt.initConfig({
|
||||
react: {
|
||||
app: {
|
||||
options: { extension: 'js' },
|
||||
files: { 'path/to/output/dir': 'path/to/jsx/templates/dir' }
|
||||
```
|
||||
>
|
||||
> It also works great with `grunt-browserify`!
|
||||
>
|
||||
> ```javascript
|
||||
browserify: {
|
||||
options: {
|
||||
transform: [ require('grunt-react').browserify ]
|
||||
},
|
||||
app: {
|
||||
src: 'path/to/source/main.js',
|
||||
dest: 'path/to/target/output.js'
|
||||
```
|
||||
>
|
||||
> [Check out the project ...](https://github.com/ericclemmons/grunt-react)
|
||||
|
||||
## Backbone/Handlebars Nested Views
|
||||
|
||||
[Joel Burget](http://joelburget.com/) wrote a blog post talking about the way we would write React-like components in Backbone and Handlebars.
|
||||
|
||||
> The problem here is that we're trying to maniplate a tree, but there's a textual layer we have to go through. Our views are represented as a tree - the subviews are children of CommentCollectionView - and they end up as part of a tree in the DOM. But there's a Handlebars layer in the middle (which deals in flat strings), so the hierarchy must be destructed and rebuilt when we render.
|
||||
>
|
||||
> What does it take to render a collection view? In the Backbone/Handlebars view of the world you have to render the template (with stubs), render each subview which replaces a stub, and keep a reference to each subview (or anything within the view that could change in the future).
|
||||
>
|
||||
> So while our view is conceptually hierarchical, due to the fact that it has to go through a flat textual representation, we need to do a lot of extra work to reassemble that structure after rendering.
|
||||
>
|
||||
> [Read the full post...](http://joelburget.com/react/)
|
||||
|
||||
## JSRomandie Meetup
|
||||
|
||||
[Renault John Lecoultre](https://twitter.com/renajohn/) from [BugBuster](http://www.bugbuster.com) did a React introduction talk at a JS meetup called [JS Romandie](https://twitter.com/jsromandie) last week.
|
||||
|
||||
<script async class="speakerdeck-embed" data-id="888a9d50c01b01300df36658d0894ac1" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"></script>
|
||||
|
||||
## CoffeeScript integration
|
||||
|
||||
[Vjeux](http://blog.vjeux.com/) used the fact that JSX is just a syntactic sugar on-top of regular JS to rewrite the React front-page examples in CoffeeScript.
|
||||
|
||||
> Multiple people asked what's the story about JSX and CoffeeScript. There is no JSX pre-processor for CoffeeScript and I'm not aware of anyone working on it. Fortunately, CoffeeScript is pretty expressive and we can play around the syntax to come up with something that is usable.
|
||||
>
|
||||
> ```javascript
|
||||
{div, h3, textarea} = React.DOM
|
||||
(div {className: 'MarkdownEditor'}, [
|
||||
(h3 {}, 'Input'),
|
||||
(textarea {onKeyUp: @handleKeyUp, ref: 'textarea'},
|
||||
@state.value
|
||||
)
|
||||
])
|
||||
```
|
||||
>
|
||||
> [Read the full post...](http://blog.vjeux.com/2013/javascript/react-coffeescript.html)
|
||||
|
||||
## Tutorial in Plain Javascript
|
||||
|
||||
We've seen a lot of people comparing React with various frameworks. [Ricardo Tomasi](http://ricardo.cc/) decided to re-implement the tutorial without any framework, just plain Javascript.
|
||||
|
||||
> Facebook & Instagram launched the React framework and an accompanying tutorial. Developer Vlad Yazhbin decided to rewrite that using AngularJS. The end result is pretty neat, but if you're like me you will not actually appreciate the HTML speaking for itself and doing all the hard work. So let's see what that looks like in plain javascript.
|
||||
>
|
||||
> [Read the full post...](http://ricardo.cc/2013/06/07/react-tutorial-rewritten-in-plain-javascript.html)
|
||||
52
docs/_posts/2013-07-02-react-v0-4-autobind-by-default.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "New in React v0.4: Autobind by Default"
|
||||
layout: post
|
||||
author: Paul O'Shannessy
|
||||
---
|
||||
|
||||
React v0.4 is very close to completion. As we finish it off, we'd like to share with you some of the major changes we've made since v0.3. This is the first of several posts we'll be making over the next week.
|
||||
|
||||
|
||||
## What is React.autoBind?
|
||||
|
||||
If you take a look at most of our current examples, you'll see us using `React.autoBind` for event handlers. This is used in place of `Function.prototype.bind`. Remember that in JS, [function calls are late-bound](http://bonsaiden.github.io/JavaScript-Garden/#function.this). That means that if you simply pass a function around, the `this` used inside won't necessarily be the `this` you expect. `Function.prototype.bind` creates a new, properly bound, function so that when called, `this` is exactly what you expect it to be.
|
||||
|
||||
Before we launched React, we would write this:
|
||||
|
||||
```js{4}
|
||||
React.createClass({
|
||||
onClick: function(event) {/* do something with this */},
|
||||
render: function() {
|
||||
return <button onClick={this.onClick.bind(this)} />;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
We wrote `React.autoBind` as a way to cache the function creation and save on memory usage. Since `render` can get called multiple times, if you used `this.onClick.bind(this)` you would actually create a new function on each pass. With React v0.3 you were able to write this instead:
|
||||
|
||||
```js{2,4}
|
||||
React.createClass({
|
||||
onClick: React.autoBind(function(event) {/* do something with this */}),
|
||||
render: function() {
|
||||
return <button onClick={this.onClick} />;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## What's Changing in v0.4?
|
||||
|
||||
After using `React.autoBind` for a few weeks, we realized that there were very few times that we didn't want that behavior. So we made it the default! Now all methods defined within `React.createClass` will already be bound to the correct instance.
|
||||
|
||||
Starting with v0.4 you can just write this:
|
||||
|
||||
```js{2,4}
|
||||
React.createClass({
|
||||
onClick: function(event) {/* do something with this */},
|
||||
render: function() {
|
||||
return <button onClick={this.onClick} />;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
For v0.4 we will simply be making `React.autoBind` a no-op — it will just return the function you pass to it. Most likely you won't have to change your code to account for this change, though we encourage you to update. We'll publish a migration guide documenting this and other changes that come along with React v0.4.
|
||||
58
docs/_posts/2013-07-03-community-roundup-4.md
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "Community Round-up #4"
|
||||
layout: post
|
||||
author: Vjeux
|
||||
---
|
||||
|
||||
React reconciliation process appears to be very well suited to implement a text editor with a live preview as people at Khan Academy show us.
|
||||
|
||||
## Khan Academy
|
||||
|
||||
[Ben Kamens](http://bjk5.com/) explains how [Ben Alpert](http://benalpert.com/) and [Joel Burget](http://joelburget.com/) are promoting React inside of [Khan Academy](https://www.khanacademy.org/). They now have three projects in the works using React.
|
||||
|
||||
> Recently two Khan Academy devs dropped into our team chat and said they were gonna use React to write a new feature. They even hinted that we may want to adopt it product-wide.
|
||||
>
|
||||
> "The library is only a week old. It's a brand new way of thinking about things. We're the first to use it outside of Facebook. Heck, even the React devs were surprised to hear we're using this in production!!!"
|
||||
>
|
||||
> [Read the full post...](http://bjk5.com/post/53742233351/getting-your-team-to-adopt-new-technology)
|
||||
|
||||
The best part is the demo of how React reconciliation process makes live editing more user-friendly.
|
||||
|
||||
> Our renderer, post-React, is on the left. A typical math editor's preview is on the right.
|
||||
> <figure>[<img src="/react/img/blog/monkeys.gif" width="70%" />](http://bjk5.com/post/53742233351/getting-your-team-to-adopt-new-technology)</figure>
|
||||
|
||||
## React Snippets
|
||||
|
||||
Over the past several weeks, members of our team, [Pete Hunt](http://www.petehunt.net/) and [Paul O'Shannessy](http://zpao.com/), answered many questions that were asked in the [React group](https://groups.google.com/forum/#!forum/reactjs). They give a good overview of how to integrate React with other libraries and APIs through the use of [Mixins](/react/docs/mixins.html) and [Lifecycle Methods](/react/docs/advanced-components.html).
|
||||
|
||||
> [Listening Scroll Event](https://groups.google.com/forum/#!topic/reactjs/l6PnP8qbofk)
|
||||
>
|
||||
> * [JSFiddle](http://jsfiddle.net/aabeL/1/): Basically I've given you two mixins. The first lets you react to global scroll events. The second is, IMO, much more useful: it gives you scroll start and scroll end events, which you can use with setState() to create components that react based on whether the user is scrolling or not.
|
||||
>
|
||||
> [Fade-in Transition](https://groups.google.com/forum/#!topic/reactjs/RVAY_eQmdpo)
|
||||
>
|
||||
> * [JSFiddle](http://jsfiddle.net/ufe8k/1/): Creating a new `<FadeInWhenAdded>` component and using jQuery `.fadeIn()` function on the DOM node.
|
||||
> * [JSFiddle](http://jsfiddle.net/R8f5L/5/): Using CSS transition instead.
|
||||
>
|
||||
> [Socket.IO Integration](https://groups.google.com/forum/#!topic/reactjs/pyUZBRWcHB4)
|
||||
>
|
||||
> * [Gist](https://gist.github.com/zpao/5686416): The big thing to notice is that my component is pretty dumb (it doesn't have to be but that's how I chose to model it). All it does is render itself based on the props that are passed in. renderOrUpdate is where the "magic" happens.
|
||||
> * [Gist](https://gist.github.com/petehunt/5687230): This example is doing everything -- including the IO -- inside of a single React component.
|
||||
> * [Gist](https://gist.github.com/petehunt/5687276): One pattern that we use at Instagram a lot is to employ separation of concerns and consolidate I/O and state into components higher in the hierarchy to keep the rest of the components mostly stateless and purely display.
|
||||
>
|
||||
> [Sortable jQuery Plugin Integration](https://groups.google.com/forum/#!topic/reactjs/mHfBGI3Qwz4)
|
||||
>
|
||||
> * [JSFiddle](http://jsfiddle.net/LQxy7/): Your React component simply render empty divs, and then in componentDidMount() you call React.renderComponent() on each of those divs to set up a new root React tree. Be sure to explicitly unmountAndReleaseReactRootNode() for each component in componentWillUnmount().
|
||||
|
||||
## Introduction to React Screencast
|
||||
|
||||
[Pete Hunt](http://www.petehunt.net/) recorded himself implementing a simple `<Blink>` tag in React.
|
||||
|
||||
<figure><iframe src="http://player.vimeo.com/video/67248575" width="500" height="340" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></figure>
|
||||
|
||||
## Snake in React
|
||||
|
||||
[Tom Occhino](http://tomocchino.com/) implemented Snake in 150 lines with React.
|
||||
|
||||
> [Check the source on Github](https://github.com/tomocchino/react-snake/blob/master/src/snake.js)
|
||||
> <figure>[](http://tomocchino.github.io/react-snake/)</figure>
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "New in React v0.4: Prop Validation and Default Values"
|
||||
layout: post
|
||||
author: Paul O'Shannessy
|
||||
---
|
||||
|
||||
Many of the questions we got following the public launch of React revolved around `props`, specifically that people wanted to do validation and to make sure their components had sensible defaults.
|
||||
|
||||
|
||||
## Validation
|
||||
|
||||
Oftentimes you want to validate your `props` before you use them. Perhaps you want to ensure they are a specific type. Or maybe you want to restrict your prop to specific values. Or maybe you want to make a specific prop required. This was always possible — you could have written validations in your `render` or `componentWillReceiveProps` functions, but that gets clunky fast.
|
||||
|
||||
React v0.4 will provide a nice easy way for you to use built-in validators, or to even write your own.
|
||||
|
||||
```js
|
||||
React.createClass({
|
||||
propTypes: {
|
||||
// An optional string prop named "description".
|
||||
description: React.PropTypes.string,
|
||||
|
||||
// A required enum prop named "category".
|
||||
category: React.PropTypes.oneOf(['News','Photos']).isRequired,
|
||||
|
||||
// A prop named "dialog" that requires an instance of Dialog.
|
||||
dialog: React.PropTypes.instanceOf(Dialog).isRequired
|
||||
},
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Default Values
|
||||
|
||||
One common pattern we've seen with our React code is to do something like this:
|
||||
|
||||
```js
|
||||
React.createClass({
|
||||
render: function() {
|
||||
var value = this.props.value || 'default value';
|
||||
return <div>{value}</div>;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Do this for a few `props` across a few components and now you have a lot of redundant code. Starting with React v0.4, you can provide default values in a declarative way:
|
||||
|
||||
```js
|
||||
React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
value: 'default value'
|
||||
};
|
||||
}
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
We will use the cached result of this function before each `render`. We also perform all validations before each `render` to ensure that you have all of the data you need in the right form before you try to use it.
|
||||
|
||||
- - -
|
||||
|
||||
Both of these features are entirely optional. We've found them to be increasingly valuable at Facebook as our applications grow and evolve, and we hope others find them useful as well.
|
||||
40
docs/_posts/2013-07-17-react-v0-4-0.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "React v0.4.0"
|
||||
layout: post
|
||||
author: Paul O'Shannessy
|
||||
---
|
||||
|
||||
Over the past 2 months we've been taking feedback and working hard to make React even better. We've also added several features that awe didn't finish in time for the v0.3 launch, and are proud to announce the availability of React v0.4 today!
|
||||
|
||||
|
||||
This release could not have happened without the support of our growing community. Since launch day, the community has contributed blog posts, questions to the [Google Group](http://groups.google.com/group/reactjs), and issues and pull requests on GitHub. We've had contributions ranging from documentation improvements to major changes to React's rendering. We've seen people integrate React into the tools they're using and the products they're building, and we're all very excited to see what our budding community builds next!
|
||||
|
||||
React v0.4 has some big changes. We've also restructured the documentation to better communicate how to use React. We've summarized the changes below and linked to documentation where we think it will be especially useful.
|
||||
|
||||
When you're ready, [go download it](/react/downloads.html)!
|
||||
|
||||
|
||||
### React
|
||||
|
||||
* Switch from using `id` attribute to `data-reactid` to track DOM nodes. This allows you to integrate with other JS and CSS libraries more easily.
|
||||
* Support for more DOM elements and attributes (e.g., `<canvas>`)
|
||||
* Improved server-side rendering APIs. `React.renderComponentToString(<component>, callback)` allows you to use React on the server and generate markup which can be sent down to the browser.
|
||||
* `prop` improvements: validation and default values. [Read our blog post for details...](http://facebook.github.io/react/blog/2013/07/11/react-v0-4-prop-validation-and-default-values.html)
|
||||
* Support for the `key` prop, which allows for finer control over reconciliation. [Read the docs for details...](http://facebook.github.io/react/docs/multiple-components.html)
|
||||
* Removed `React.autoBind`. [Read our blog post for details...](http://facebook.github.io/react/blog/2013/07/02/react-v0-4-autobind-by-default.html)
|
||||
* Improvements to forms. We've written wrappers around `<input>`, `<textarea>`, `<option>`, and `<select>` in order to standardize many inconsistencies in browser implementations. This includes support for `defaultValue`, and improved implementation of the `onChange` event, and circuit completion. [Read the docs for details...](http://facebook.github.io/react/docs/forms.html)
|
||||
* We've implemented an improved synthetic event system that conforms to the W3C spec.
|
||||
* Updates to your component are batched now, which may result in a significantly faster re-render of components. `this.setState` now takes an optional callback as it's second parameter. If you were using `onClick={this.setState.bind(this, state)}` previously, you'll want to make sure you add a third parameter so that the event is not treated as the callback.
|
||||
|
||||
### JSX
|
||||
|
||||
* Support for comment nodes `<div>{/* this is a comment and won't be rendered */}</div>`
|
||||
* Children are now transformed directly into arguments instead of being wrapped in an array
|
||||
E.g. `<div><Component1/><Component2></div>` is transformed into `React.DOM.div(null, Component1(null), Component2(null))`.
|
||||
Previously this would be transformed into `React.DOM.div(null, [Component1(null), Component2(null)])`.
|
||||
If you were using React without JSX previously, your code should still work.
|
||||
|
||||
### react-tools
|
||||
|
||||
* Fixed a number of bugs when transforming directories
|
||||
* No longer re-write `require()`s to be relative unless specified
|
||||
28
docs/blog/index.html
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Blog
|
||||
layout: default
|
||||
sectionid: blog
|
||||
---
|
||||
|
||||
<section class="content wrap blogContent">
|
||||
{% include nav_blog.html %}
|
||||
<div class="inner-content">
|
||||
{% for page in site.posts %}
|
||||
<div class="post-list-item">
|
||||
<h1><a href="/react{{ page.url }}">{{ page.title }}</a></h1>
|
||||
<p class="meta">{{ page.date | date: "%B %e, %Y" }} by {{ page.author }}</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="post">
|
||||
{{ page.excerpt }}
|
||||
{% if page.excerpt != page.content %}
|
||||
<p>
|
||||
<a href="/react{{ page.url }}">Continue Reading →</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
30
docs/docs/01-why-react.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
id: why-react
|
||||
title: Why React?
|
||||
layout: docs
|
||||
permalink: why-react.html
|
||||
next: displaying-data.html
|
||||
---
|
||||
React is a JavaScript library for creating user interfaces by Facebook and Instagram. Many people choose to think of React as the **V** in **[MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)**.
|
||||
|
||||
We built React to solve one problem: **building large applications with data that changes over time**. To do this, React uses two main ideas.
|
||||
|
||||
### Simple
|
||||
|
||||
Simply express how your app should look at any given point in time, and React will automatically manage all UI updates when your underlying data changes.
|
||||
|
||||
### Declarative
|
||||
|
||||
When the data changes, React conceptually hits the "refresh" button, and knows to only update the changed parts.
|
||||
|
||||
## Build Composable Components
|
||||
|
||||
React is all about building reusable components. In fact, with React the *only* thing you do is build components. Since they're so encapsulated, components make code reuse, testing, and separation of concerns easy.
|
||||
|
||||
## Give It Five Minutes
|
||||
|
||||
React challenges a lot of conventional wisdom, and at first glance some of the ideas may seem crazy. [Give it five minutes](http://37signals.com/svn/posts/3124-give-it-five-minutes) while reading this guide; those crazy ideas have worked for building thousands of components both inside and outside of Facebook and Instagram.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more about our motivations behind building React in [this blog post](http://facebook.github.io/react/blog/2013/06/05/why-react.html).
|
||||
91
docs/docs/02-displaying-data.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
id: displaying-data
|
||||
title: Displaying Data
|
||||
layout: docs
|
||||
permalink: displaying-data.html
|
||||
prev: why-react.html
|
||||
next: jsx-in-depth.html
|
||||
---
|
||||
|
||||
The most basic thing you can do with a UI is display some data. React makes it easy to display data and automatically keeps the interface up-to-date when the data changes.
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
Let's look at a really simple example. Create a `hello-react.html` file with the following code:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Hello React</title>
|
||||
<script src="http://fb.me/react-{{site.react_version}}.min.js"></script>
|
||||
<script src="http://fb.me/JSXTransformer-{{site.react_version}}.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="example"></div>
|
||||
<script type="text/jsx">
|
||||
|
||||
// ** Your code goes here! **
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
For the rest of the documentation, we'll just focus on the JavaScript code and assume it's inserted into a template like the one above. Replace the placeholder comment above with the following JS:
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var HelloWorld = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<p>
|
||||
Hello, <input type="text" placeholder="Your name here" />!
|
||||
It is {this.props.date.toTimeString()}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
setInterval(function() {
|
||||
React.renderComponent(
|
||||
<HelloWorld date={new Date()} />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
}, 500);
|
||||
```
|
||||
|
||||
|
||||
## Reactive Updates
|
||||
|
||||
Open `hello-react.html` in a web browser and type your name into the text field. Notice that React is only changing the time string in the UI — any input you put in the text field remains, even though you haven't written any code to manage this behavior. React figures it out for you and does the right thing.
|
||||
|
||||
The way we are able to figure this out is that React does not manipulate the DOM unless it needs to. **It uses a fast, internal mock DOM to perform diffs and computes the most efficient DOM mutation for you.**
|
||||
|
||||
The inputs to this component are called `props` — short for "properties". They're passed as attributes in JSX syntax. You should think of these as immutable within the component, that is, **never write to `this.props`**.
|
||||
|
||||
|
||||
## Components are Just Like Functions
|
||||
|
||||
React components are very simple. You can think of them as simple function that take in `props` and `state` (discussed later) and render HTML. Because they're so simple, it makes them very easy to reason about.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> **One limitation**: React components can only render a single root node. If you want to return multiple nodes they *must* be wrapped in a single root.
|
||||
|
||||
|
||||
## JSX Syntax
|
||||
|
||||
We strongly believe that components are the right way to separate concerns rather than "templates" and "display logic." We think that markup and the code that generates it are intimately tied together. Additionally, display logic is often very complex and using template languages to express it becomes cumbersome.
|
||||
|
||||
We've found that the best solution for this problem is to generate markup directly from the JavaScript code such that you can use all of the expressive power of a real programming language to build UIs. In order to make this easier, we've added a very simple, **optional** HTML-like syntax for the function calls that generate markup called JSX.
|
||||
|
||||
**JSX lets you write JavaScript function calls with HTML syntax.** To generate a link in React using pure JavaScript you'd write: `React.DOM.a({href: 'http://facebook.github.io/react/'}, 'Hello React!')`. With JSX this becomes `<a href="http://facebook.github.io/react/">Hello React!</a>`. We've found this has made building React apps easier and designers tend to prefer the syntax, but everyone has their own workflow, so **JSX is not required to use React.**
|
||||
|
||||
JSX is very small; the "hello, world" example above uses every feature of JSX. To learn more about it, see [JSX in depth](./jsx-in-depth.html). Or see the transform in action in [our live JSX compiler](/react/jsx-compiler.html).
|
||||
|
||||
JSX is similar to HTML, but not exactly the same. See [JSX gotchas](./jsx-gotchas.html) for some key differences.
|
||||
|
||||
The easiest way to get started with JSX is to use the in-browser `JSXTransformer`. We strongly recommend that you don't use this in production. You can precompile your code using our command-line [react-tools](http://npmjs.org/package/react-tools) package.
|
||||
@@ -1,29 +1,43 @@
|
||||
---
|
||||
id: docs-syntax
|
||||
title: JSX Syntax
|
||||
description: Writing JavaScript with XML syntax.
|
||||
id: jsx-in-depth
|
||||
title: JSX in Depth
|
||||
layout: docs
|
||||
prev: common-questions.html
|
||||
next: component-basics.html
|
||||
permalink: jsx-in-depth.html
|
||||
prev: displaying-data.html
|
||||
next: jsx-gotchas.html
|
||||
---
|
||||
|
||||
JSX is a JavaScript XML syntax extension recommended (but not required) for use
|
||||
JSX is a JavaScript XML syntax transform recommended for use
|
||||
with React.
|
||||
|
||||
JSX makes code that deeply nests React components more readable, and writing it
|
||||
feels like writing HTML. React documentation examples make use of JSX.
|
||||
|
||||
## Why JSX?
|
||||
|
||||
React works out of the box without JSX. Simply construct your markup using the
|
||||
functions on `React.DOM`. For example, here's how to construct a simple link:
|
||||
|
||||
```javascript
|
||||
var link = React.DOM.a({href: 'http://facebook.github.io/react'}, 'React');
|
||||
```
|
||||
|
||||
We recommend using JSX for many reasons:
|
||||
|
||||
* It's easier to visualize the structure of the DOM.
|
||||
* Designers are more comfortable making changes.
|
||||
* It's familiar for those who have used MXML or XAML.
|
||||
|
||||
|
||||
## The Transform
|
||||
|
||||
JSX transforms XML-like syntax into native JavaScript. It turns XML elements and
|
||||
attributes into function calls and objects, respectively.
|
||||
JSX transforms from an XML-like syntax into native JavaScript. XML elements and
|
||||
attributes are transformed into function calls and objects, respectively.
|
||||
|
||||
```javascript
|
||||
var Nav;
|
||||
// Input (JSX):
|
||||
var app = <Nav color="blue" />;
|
||||
// Output (JS):
|
||||
var app = Nav({color:'blue'});
|
||||
var app = Nav({color:"blue"});
|
||||
```
|
||||
|
||||
Notice that in order to use `<Nav />`, the `Nav` variable must be in scope.
|
||||
@@ -35,27 +49,31 @@ var Nav, Profile;
|
||||
// Input (JSX):
|
||||
var app = <Nav color="blue"><Profile>click</Profile></Nav>;
|
||||
// Output (JS):
|
||||
var app = Nav({color:'blue'}, Profile({}, 'click'));
|
||||
var app = Nav({color:"blue"}, Profile(null, "click"));
|
||||
```
|
||||
|
||||
The [Getting Started](getting-started.html) guide shows how to setup JSX
|
||||
compilation.
|
||||
Use the [JSX Compiler](/react/jsx-compiler.html) to try out JSX and see how it
|
||||
desugars into native JavaScript.
|
||||
|
||||
If you want to use JSX, the [Getting Started](getting-started.html) guide shows
|
||||
how to setup compilation.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> Details about the code transform are given here to increase understanding, but
|
||||
> your code should not rely on these implementation details.
|
||||
|
||||
|
||||
## React and JSX
|
||||
|
||||
React and JSX are independent technologies, but JSX was primarily built with
|
||||
React in mind. The two valid uses of JSX are:
|
||||
|
||||
- To construct instances of React DOM components (`React.DOM.*`).
|
||||
- To construct instances of composite components created with
|
||||
* To construct instances of React DOM components (`React.DOM.*`).
|
||||
* To construct instances of composite components created with
|
||||
`React.createClass()`.
|
||||
|
||||
**React DOM Components**
|
||||
### React DOM Components
|
||||
|
||||
To construct a `<div>` is to create a variable that refers to `React.DOM.div`.
|
||||
|
||||
@@ -64,7 +82,7 @@ var div = React.DOM.div;
|
||||
var app = <div className="appClass">Hello, React!</div>;
|
||||
```
|
||||
|
||||
**React Component Components**
|
||||
### React Component Components
|
||||
|
||||
To construct an instance of a composite component, create a variable that
|
||||
references the class.
|
||||
@@ -97,7 +115,7 @@ var Nav;
|
||||
// Input (JSX):
|
||||
var tree = <Nav><span /></Nav>;
|
||||
// Output (JS):
|
||||
var tree = Nav({}, React.DOM.span({}));
|
||||
var tree = Nav(null, React.DOM.span(null));
|
||||
```
|
||||
|
||||
> Remember:
|
||||
@@ -108,7 +126,7 @@ var tree = Nav({}, React.DOM.span({}));
|
||||
|
||||
## JavaScript Expressions
|
||||
|
||||
#### Attribute Expressions
|
||||
### Attribute Expressions
|
||||
|
||||
To use a JavaScript expression as an attribute value, wrap the expression in a
|
||||
pair of curly braces (`{}`) instead of quotes (`""`).
|
||||
@@ -120,7 +138,7 @@ var person = <Person name={window.isLoggedIn ? window.name : ''} />;
|
||||
var person = Person({name: window.isLoggedIn ? window.name : ''});
|
||||
```
|
||||
|
||||
#### Child Expressions
|
||||
### Child Expressions
|
||||
|
||||
Likewise, JavaScript expressions may be used to express children:
|
||||
|
||||
@@ -128,16 +146,24 @@ Likewise, JavaScript expressions may be used to express children:
|
||||
// Input (JSX):
|
||||
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
|
||||
// Output (JS):
|
||||
var content = Container({}, window.isLoggedIn ? <Nav /> : <Login />);
|
||||
var content = Container(null, window.isLoggedIn ? Nav(null) : Login(null));
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
It's easy to add comments within your JSX; they're just JS expressions:
|
||||
|
||||
```javascript
|
||||
var content = <Container>{/* this is a comment */}<Nav /></Container>;
|
||||
```
|
||||
|
||||
## Tooling
|
||||
|
||||
Beyond the compilation step, JSX does not require any special tools.
|
||||
|
||||
- Many editors already include reasonable support for JSX (Vim, Emacs js2-mode).
|
||||
- Linting provides accurate line numbers after compiling without sourcemaps.
|
||||
- Elements use standard scoping so linters can find usage of out-of-scope
|
||||
* Many editors already include reasonable support for JSX (Vim, Emacs js2-mode).
|
||||
* Linting provides accurate line numbers after compiling without sourcemaps.
|
||||
* Elements use standard scoping so linters can find usage of out-of-scope
|
||||
components.
|
||||
|
||||
## Prior Work
|
||||
@@ -146,6 +172,8 @@ JSX is similar to several other JavaScript embedded XML language
|
||||
proposals/projects. Some of the features of JSX that distinguish it from similar
|
||||
efforts include:
|
||||
|
||||
- JSX is a simple syntactic transform.
|
||||
- JSX neither provides nor requires a runtime library.
|
||||
- JSX does not alter or add to the semantics of JavaScript.
|
||||
* JSX is a simple syntactic transform.
|
||||
* JSX neither provides nor requires a runtime library.
|
||||
* JSX does not alter or add to the semantics of JavaScript.
|
||||
|
||||
JSX is similar to HTML, but not exactly the same. See [JSX gotchas](./jsx-gotchas.html) for some key differences.
|
||||
77
docs/docs/02.2-jsx-gotchas.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
id: jsx-gotchas
|
||||
title: JSX Gotchas
|
||||
layout: docs
|
||||
permalink: jsx-gotchas.html
|
||||
prev: jsx-in-depth.html
|
||||
next: interactivity-and-dynamic-uis.html
|
||||
---
|
||||
|
||||
JSX looks like HTML but there are some important differences you may run into.
|
||||
|
||||
|
||||
## Whitespace Removal
|
||||
|
||||
JSX doesn't follow the same whitespace elimination rules as HTML. JSX removes all whitespace between two curly braces expressions. If you want to have whitespace, simply add `{' '}`.
|
||||
|
||||
```javascript
|
||||
<div>{this.props.name} {' '} {this.props.surname}</div>
|
||||
```
|
||||
|
||||
Follow [Issue #65](https://github.com/facebook/react/issues/65) for discussion on this behavior.
|
||||
|
||||
|
||||
## HTML Entities
|
||||
|
||||
You can insert HTML entities within literal text in JSX:
|
||||
|
||||
```javascript
|
||||
<div>First · Second</div>
|
||||
```
|
||||
|
||||
If you want to display an HTML entity within dynamic content, you will run into double escaping issues as React escapes all the strings you are displaying in order to prevent a wide range of XSS attacks by default.
|
||||
|
||||
```javascript
|
||||
// Bad: It displays "First · Second"
|
||||
<div>{'First · Second'}</div>
|
||||
```
|
||||
|
||||
There are various ways to work-around this issue. The easiest one is to write unicode character directly in Javascript. You need to make sure that the file is saved as UTF-8 and that the proper UTF-8 directives are set so the browser will display it correctly.
|
||||
|
||||
```javascript
|
||||
<div>{'First · Second'}</div>
|
||||
```
|
||||
|
||||
A safer alternative is to find the [unicode number corresponding to the entity](http://www.fileformat.info/info/unicode/char/b7/index.htm) and use it inside of a JavaScript string.
|
||||
|
||||
```javascript
|
||||
<div>{'First \u00b7 Second'}</div>
|
||||
<div>{'First ' + String.fromCharCode(183) + ' Second'}</div>
|
||||
```
|
||||
|
||||
You can use mixed arrays with strings and JSX elements.
|
||||
|
||||
```javascript
|
||||
<div>{['First ', <span>·</span>, ' Second']}</div>
|
||||
```
|
||||
|
||||
As a last resort, you always have the ability to insert raw HTML.
|
||||
|
||||
```javascript
|
||||
<div dangerouslySetInnerHTML={{'{{'}}__html: 'First · Second'}} />
|
||||
```
|
||||
|
||||
|
||||
## Custom HTML Attributes
|
||||
|
||||
If you pass properties to native HTML elements that do not exist in the HTML specification, React will not render them. If you want to use a custom attribute, you should prefix it with `data-`.
|
||||
|
||||
```javascript
|
||||
<div data-custom-attribute="foo" />
|
||||
```
|
||||
|
||||
[Web Accessibility](http://www.w3.org/WAI/intro/aria) attributes starting with `aria-` will be rendered properly.
|
||||
|
||||
```javascript
|
||||
<div aria-hidden={true} />
|
||||
```
|
||||
89
docs/docs/03-interactivity-and-dynamic-uis.md
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
id: interactivity-and-dynamic-uis
|
||||
title: Interactivity and Dynamic UIs
|
||||
layout: docs
|
||||
permalink: interactivity-and-dynamic-uis.html
|
||||
prev: jsx-gotchas.html
|
||||
next: multiple-components.html
|
||||
---
|
||||
|
||||
You've already [learned how to display data](./displaying-data.html) with React. Now let's look at how to make our UIs interactive.
|
||||
|
||||
|
||||
## A Simple Example
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var LikeButton = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {liked: false};
|
||||
},
|
||||
handleClick: function(event) {
|
||||
this.setState({liked: !this.state.liked});
|
||||
},
|
||||
render: function() {
|
||||
var text = this.state.liked ? 'like' : 'unlike';
|
||||
return (
|
||||
<p onClick={this.handleClick}>
|
||||
You {text} this. Click to toggle.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(
|
||||
<LikeButton />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
## Event Handling and Synthetic Events
|
||||
|
||||
With React you simply pass your event handler as a camelCased prop similar to how you'd do it in normal HTML. React ensures that all events behave identically in IE8 and above by implementing a synthetic event system. That is, React knows how to bubble and capture events according to the spec, and the events passed to your event handler are guaranteed to be consistent with [the W3C spec](http://www.w3.org/TR/DOM-Level-3-Events/), regardless of which browser you're using.
|
||||
|
||||
If you'd like to use React on a touch device (i.e. a phone or tablet), simply call `React.initializeTouchEvents(true);` to turn them on.
|
||||
|
||||
|
||||
## Under the Hood: autoBind and Event Delegation
|
||||
|
||||
Under the hood React does a few things to keep your code performant and easy to understand.
|
||||
|
||||
**Autobinding:** When creating callbacks in JavaScript you usually need to explicitly bind a method to its instance such that the value of `this` is correct. With React, every method is automatically bound to its component instance. React caches the bound method such that it's extremely CPU and memory efficient. It's also less typing!
|
||||
|
||||
**Event delegation:** React doesn't actually attach event handlers to the nodes themselves. When React starts up, it starts listening for all events at the top level using a single event listener. When a component is mounted or unmounted, the event handlers are simply added or removed from an internal mapping. When an event occurs, React knows how to dispatch it using this mapping. When there are no event handlers left in the mapping, React's event handlers are simple no-ops. To learn more about why this is fast, see [David Walsh's excellent blog post](http://davidwalsh.name/event-delegate).
|
||||
|
||||
|
||||
## Components are Just State Machines
|
||||
|
||||
React thinks of UIs as simple state machines. By thinking of a UI as being in various states and rendering those states, it's easy to keep your UI consistent.
|
||||
|
||||
In React, you simply update a component's state, and then render a new UI based on this new state. React takes care of updating the DOM for you in the most efficient way.
|
||||
|
||||
|
||||
## How State Works
|
||||
|
||||
A common way to inform React of a data change is by calling `setState(data, callback)`. This method merges `data` into `this.state` and re-renders the component. When the component finishes re-rendering, the optional `callback` is called. Most of the time you'll never need to provide a `callback` since React will take care of keeping your UI up-to-date for you.
|
||||
|
||||
|
||||
## What Components Should Have State?
|
||||
|
||||
Most of your components should simply take some data from `props` and render it. However, sometimes you need to respond to user input, a server request or the passage of time. For this you use state.
|
||||
|
||||
**Try to keep as many of your components as possible stateless.** By doing this you'll isolate the state to its most logical place and minimize redundancy, making it easier to reason about your application.
|
||||
|
||||
A common pattern is to create several stateless components that just render data, and have a stateful component above them in the hierarchy that passes its state to its children via `props`. The stateful component encapsulates all of the interaction logic, while the stateless components take care of rendering data in a declarative way.
|
||||
|
||||
|
||||
## What *Should* Go in State?
|
||||
|
||||
**State should contain data that a component's event handlers may change to trigger a UI update.** In real apps this data tends to be very small and JSON-serializable. When building a stateful component, think about the minimal possible representation of its state, and only store those properties in `this.state`. Inside of `render()` simply compute any other information you need based on this state. You'll find that thinking about and writing applications in this way tends to lead to the most correct application, since adding redundant or computed values to state means that you need to explicitly keep them in sync rather than rely on React computing them for you.
|
||||
|
||||
## What *Shouldn't* Go in State?
|
||||
|
||||
`this.state` should only contain the minimal amount of data needed to represent your UI's state. As such, it should not contain:
|
||||
|
||||
* **Computed data:** Don't worry about precomputing values based on state — it's easier to ensure that your UI is consistent if you do all computation within `render()`. For example, if you have an array of list items in state and you want to render the count as a string, simply render `this.state.listItems.length + ' list items'` in your `render()` method rather than storing it on state.
|
||||
* **React components:** Build them in `render()` based on underlying props and state.
|
||||
* **Duplicated data from props:** Try to use props as the source of truth where possible. Because props can change over time, it's appropriate to store props in state to be able to know its previous values.
|
||||
151
docs/docs/04-multiple-components.md
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
id: multiple-components
|
||||
title: Multiple Components
|
||||
layout: docs
|
||||
permalink: multiple-components.html
|
||||
prev: interactivity-and-dynamic-uis.html
|
||||
next: reusable-components.html
|
||||
---
|
||||
|
||||
So far, we've looked at how to write a single component to display data and handle user input. Next let's examine one of React's finest features: composability.
|
||||
|
||||
|
||||
## Motivation: Separation of Concerns
|
||||
|
||||
By building modular components that reuse other components with well-defined interfaces, you get much of the same benefits that you get by using functions or classes. Specifically you can *separate the different concerns* of your app however you please simply by building new components. By building a custom component library for your application, you are expressing your UI in a way that best fits your domain.
|
||||
|
||||
|
||||
## Composition Example
|
||||
|
||||
Let's create a simple Avatar component which shows a profile picture and username using the Facebook Graph API.
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var Avatar = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<ProfilePic username={this.props.username} />
|
||||
<ProfileLink username={this.props.username} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ProfilePic = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<img src={'http://graph.facebook.com/' + this.props.username + '/picture'} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ProfileLink = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<a href={'http://www.facebook.com/' + this.props.username}>
|
||||
{this.props.username}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(
|
||||
<Avatar username="pwh" />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
## Ownership
|
||||
|
||||
In the above example, instances of `Avatar` *own* instances of `ProfilePic` and `ProfileLink`. In React, **an owner is the component that sets the `props` of other components**. More formally, if a component `X` is created in component `Y`'s `render()` method, it is said that `X` is *owned by* `Y`. As discussed earlier, a component cannot mutate its `props` — they are always consistent with what its owner sets them to. This key property leads to UIs that are guaranteed to be consistent.
|
||||
|
||||
It's important to draw a distinciton between the owner-ownee relationship and the parent-child relationship. The owner-ownee relationship is specific to React, while the parent-child relationship is simply the one you know and love from the DOM. In the example above, `Avatar` owns the `div`, `ProfilePic` and `ProfileLink` instances, and `div` is the **parent** (but not owner) of the `ProfilePic` and `ProfileLink` instances.
|
||||
|
||||
|
||||
## Children
|
||||
|
||||
When you create a React component instance, you can include additional React components or JavaScript expressions between the opening and closing tags like this:
|
||||
|
||||
```javascript
|
||||
<Parent><Child /></Parent>
|
||||
```
|
||||
|
||||
`Parent` can read its children by accessing the special `this.props.children` prop.
|
||||
|
||||
|
||||
### Child Reconciliation
|
||||
|
||||
**Reconciliation is the process by which React updates the DOM with each new render pass.** In general, children are reconciled according to the order in which they are rendered. For example, suppose two render passes generate the following respective markup:
|
||||
|
||||
```html
|
||||
// Render Pass 1
|
||||
<Card>
|
||||
<p>Paragraph 1</p>
|
||||
<p>Paragraph 2</p>
|
||||
</Card>
|
||||
// Render Pass 2
|
||||
<Card>
|
||||
<p>Paragraph 2</p>
|
||||
</Card>
|
||||
```
|
||||
|
||||
Intuitively, `<p>Paragraph 1</p>` was removed. Instead, React will reconcile the DOM by changing the text content of the first child and destroying the last child. React reconciles according to the *order* of the children.
|
||||
|
||||
|
||||
### Stateful Children
|
||||
|
||||
For most components, this is not a big deal. However, for stateful components that maintain data in `this.state` across render passes, this can be very problematic.
|
||||
|
||||
In most cases, this can be sidestepped by hiding elements instead of destroying them:
|
||||
|
||||
```html
|
||||
// Render Pass 1
|
||||
<Card>
|
||||
<p>Paragraph 1</p>
|
||||
<p>Paragraph 2</p>
|
||||
</Card>
|
||||
// Render Pass 2
|
||||
<Card>
|
||||
<p style={{'{{'}}display: 'none'}}>Paragraph 1</p>
|
||||
<p>Paragraph 2</p>
|
||||
</Card>
|
||||
```
|
||||
|
||||
|
||||
### Dynamic Children
|
||||
|
||||
The situation gets more complicated when the children are shuffled around (as in search results) or if new components are added onto the front of the list (as in streams). In these cases where the identity and state of each child must be maintained across render passes, you can uniquely identify each child by assigning it a `key`:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
var results = this.props.results;
|
||||
return (
|
||||
<ol>
|
||||
{this.results.map(function(result) {
|
||||
return <li key={result.id}>{result.text}</li>;
|
||||
})}
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
When React reconciles the keyed children, it will ensure that any child with `key` will be reordered (instead of clobbered) or destroyed (instead of reused).
|
||||
|
||||
|
||||
## Data Flow
|
||||
|
||||
In React, data flows from owner to owned component through `props` as discussed above. This is effectively one-way data binding: owners bind their owned component's props to some value the owner has computed based on its `props` or `state`. Since this process happens recursively, data changes are automatically reflected everywhere they are used.
|
||||
|
||||
|
||||
## A Note on Performance
|
||||
|
||||
You may be thinking that it's expensive to react to changing data if there are a large number of nodes under an owner. The good news is that JavaScript is fast and `render()` methods tend to be quite simple, so in most applications this is extremely fast. Additionally, the bottleneck is almost always the DOM mutation and not JS execution and React will optimize this for you using batching and change detection.
|
||||
|
||||
However, sometimes you really want to have fine-grained control over your performance. In that case, simply override `shouldComponentUpdate()` to return false when you want React to skip processing of a subtree. See [the React reference docs](./reference.html) for more information.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> If `shouldComponentUpdate()` returns false when data has actually changed, React can't keep your UI in sync. Be sure you know what you're doing while using it, and only use this function when you have a noticeable performance problem. Don't underestimate how fast JavaScript is relative to the DOM.
|
||||
143
docs/docs/05-reusable-components.md
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
id: reusable-components
|
||||
title: Reusable Components
|
||||
layout: docs
|
||||
permalink: reusable-components.html
|
||||
prev: multiple-components.html
|
||||
next: forms.html
|
||||
---
|
||||
|
||||
When designing interfaces, break down the common design elements (buttons, form fields, layout components, etc) into reusable components with well-defined interfaces. That way, the next time you need to build some UI you can write much less code, which means faster development time, less bugs, and less bytes down the wire.
|
||||
|
||||
|
||||
## Prop Validation
|
||||
|
||||
As your app grows it's helpful to ensure that your components are used correctly. We do this by allowing you to specify `propTypes`. `React.PropTypes` exports a range of validators that can be used to make sure the data you receive is valid. When an invalid value is provided for a prop, an error will be thrown. Here is an example documenting the different validators provided:
|
||||
|
||||
```javascript
|
||||
React.createClass({
|
||||
propTypes: {
|
||||
// You can declare that a prop is a specific JS primitive. By default, these
|
||||
// are all optional.
|
||||
optionalArray: React.PropTypes.array,
|
||||
optionalBool: React.PropTypes.bool,
|
||||
optionalFunc: React.PropTypes.func,
|
||||
optionalNumber: React.PropTypes.number,
|
||||
optionalObject: React.PropTypes.object,
|
||||
optionalString: React.PropTypes.string,
|
||||
|
||||
// You can ensure that your prop is limited to specific values by treating
|
||||
// it as an enum.
|
||||
optionalEnum: React.PropTypes.oneOf(['News','Photos']),
|
||||
|
||||
// You can also declare that a prop is an instance of a class. This uses
|
||||
// JS's instanceof operator.
|
||||
someClass: React.PropTypes.instanceOf(SomeClass),
|
||||
|
||||
// You can chain any of the above with isRequired to make sure an error is
|
||||
// thrown if the prop isn't provide.
|
||||
requiredFunc: React.PropTypes.func.isRequired
|
||||
|
||||
// You can also specify a custom validator.
|
||||
customProp: function(props, propName, componentName) {
|
||||
if (!/matchme/.test(props[propName])) {
|
||||
throw new Error('Validation failed!')
|
||||
}
|
||||
}
|
||||
},
|
||||
/* ... */
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Default Prop Values
|
||||
|
||||
React lets you define default values for your `props` in a very declarative way:
|
||||
|
||||
```javascript
|
||||
var ComponentWithDefaultProps = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
value: 'default value'
|
||||
};
|
||||
}
|
||||
/* ... */
|
||||
});
|
||||
```
|
||||
|
||||
The result of `getDefaultProps()` will be cached and used to ensure that `this.props.value` will have a value if it was not specified by the parent component. This allows you to safely just use your props without having to write repetitive and fragile code to handle that yourself.
|
||||
|
||||
|
||||
## Transferring Props: A Shortcut
|
||||
|
||||
A common type of React component is one that extends a basic HTML in a simple way. Often you'll want to copy any HTML attributes passed to your component to the underlying HTML element to save typing. React provides `transferPropsTo()` to do just this.
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var CheckLink = React.createClass({
|
||||
render: function() {
|
||||
// transferPropsTo() will take any props pased to CheckLink
|
||||
// and copy them to <a>
|
||||
return this.transferPropsTo(<a>{'√ '}{this.props.children}</a>);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(
|
||||
<CheckLink href="javascript:alert('Hello, world!');">
|
||||
Click here!
|
||||
</CheckLink>,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
## Mixins
|
||||
|
||||
Components are the best way to reuse code in React, but sometimes very different components may share some common functionality. These are sometimes called [cross-cutting concerns](http://en.wikipedia.org/wiki/Cross-cutting_concern). React provides `mixins` to solve this problem.
|
||||
|
||||
One common use case is a component wanting to update itself on a time interval. It's easy to use `setInterval()`, but it's important to cancel your interval when you don't need it anymore to save memory. React provides [lifecycle methods](./working-with-the-browser.html) that let you know when a component is about to be created or destroyed. Let's create a simple mixin that uses these methods to provide an easy `setInterval()` function that will automatically get cleaned up when your component is destroyed.
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var SetIntervalMixin = {
|
||||
componentWillMount: function() {
|
||||
this.intervals = [];
|
||||
},
|
||||
setInterval: function() {
|
||||
this.intervals.push(setInterval.apply(null, arguments));
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
this.intervals.map(clearInterval);
|
||||
}
|
||||
};
|
||||
|
||||
var TickTock = React.createClass({
|
||||
mixins: [SetIntervalMixin], // Use the mixin
|
||||
getInitialState: function() {
|
||||
return {seconds: 0};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.setInterval(this.tick, 1000); // Call a method on the mixin
|
||||
},
|
||||
tick: function() {
|
||||
this.setState({seconds: this.state.seconds + 1});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<p>
|
||||
React has been running for {this.state.seconds} seconds.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(
|
||||
<TickTock />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
A nice feature of mixins is that if a component is using multiple mixins and several mixins define the same lifecycle method (i.e. several mixins want to do some cleanup when the component is destroyed), all of the lifecycle methods are guaranteed to be called.
|
||||
|
||||
132
docs/docs/06-forms.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
id: forms
|
||||
title: Forms
|
||||
layout: docs
|
||||
permalink: forms.html
|
||||
prev: reusable-components.html
|
||||
next: working-with-the-browser.html
|
||||
---
|
||||
|
||||
Form components such as `<input>`, `<textarea>`, and `<option>` differ from other native components because they can be mutated via user interactions. These components provide interfaces that make it easier to manage forms in response to user interactions.
|
||||
|
||||
|
||||
## Interactive Props
|
||||
|
||||
Form components support a few props that are affected via user interactions:
|
||||
|
||||
* `value`, supported by `<input>` and `<textarea>` components.
|
||||
* `checked`, supported by `<input>` components of type `checkbox` or `radio`.
|
||||
* `selected`, supported by `<option>` components.
|
||||
|
||||
In HTML, the value of `<textarea>` is set via children. In React, you should use `value` instead.
|
||||
|
||||
Form components allow listening for changes by setting a callback to the `onChange` prop. The `onChange` prop works across browsers to fire in response to user interactions when:
|
||||
|
||||
* The `value` of `<input>` or `<textarea>` changes.
|
||||
* The `checked` state of `<input>` changes.
|
||||
* The `selected` state of `<option>` changes.
|
||||
|
||||
Like all DOM events, the `onChange` prop is supported on all native components and can be used to listen to bubbled change events.
|
||||
|
||||
|
||||
## Controlled Components
|
||||
|
||||
An `<input>` with `value` set is a *controlled* component. In a controlled `<input>`, the value of the rendered element will always reflect the `value` prop. For example:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
return <input type="text" value="Hello!" />;
|
||||
}
|
||||
```
|
||||
|
||||
This will render an input that always has a value of `Hello!`. Any user input will have no effect on the rendered element because React has declared the value to be `Hello!`. If you wanted to update the value in response to user input, you could use the `onChange` event:
|
||||
|
||||
```javascript
|
||||
getInitialState: function() {
|
||||
return {value: 'Hello!'};
|
||||
},
|
||||
handleChange: function(event) {
|
||||
this.setState({value: event.target.value});
|
||||
},
|
||||
render: function() {
|
||||
var value = this.state.value;
|
||||
return <input type="text" value={value} onChange={this.handleChange} />;
|
||||
}
|
||||
```
|
||||
|
||||
In this example, we are simply accepting the newest value provided by the user and updating the `value` prop of the `<input>` component. This pattern makes it easy to implement interfaces that respond to or validate user interactions. For example:
|
||||
|
||||
```javascript
|
||||
handleChange: function(event) {
|
||||
this.setState({value: event.target.value.substr(0, 140)});
|
||||
}
|
||||
```
|
||||
|
||||
This would accept user input but truncate the value to the first 140 characters.
|
||||
|
||||
|
||||
## Uncontrolled Components
|
||||
|
||||
An `<input>` that does not supply a `value` (or sets it to `null`) is an *uncontrolled* component. In an uncontrolled `<input>`, the value of the rendered element will reflect the user's input. For example:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
return <input type="text" />;
|
||||
}
|
||||
```
|
||||
|
||||
This will render an input that starts off with an empty value. Any user input will be immediately reflected by the rendered element. If you wanted to listen to updates to the value, you could use the `onChange` event just like you can with controlled components.
|
||||
|
||||
If you want to initialize the component with a non-empty value, you can supply a `defaultValue` prop. For example:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
return <input type="text" defaultValue="Hello!" />;
|
||||
}
|
||||
```
|
||||
|
||||
This example will function much like the **Controlled Components** example above.
|
||||
|
||||
Likewise, `<input>` supports `defaultChecked` and `<option>` supports `defaultSelected`.
|
||||
|
||||
|
||||
## Advanced Topics
|
||||
|
||||
|
||||
### Why Controlled Components?
|
||||
|
||||
Using form components such as `<input>` in React presents a challenge that is absent when writing traditional form HTML. For example, in HTML:
|
||||
|
||||
```html
|
||||
<input type="text" name="title" value="Untitled" />
|
||||
```
|
||||
|
||||
This renders an input *initialized* with the value, `Untitled`. When the user updates the input, the node's value *property* will change. However, `node.getAttribute('value')` will still return the value used at initialization time, `Untitled`.
|
||||
|
||||
Unlike HTML, React components must represent the state of the view at any point in time and not only at initialization time. For example, in React:
|
||||
|
||||
```javascript
|
||||
render: function() {
|
||||
return <input type="text" name="title" value="Untitled" />;
|
||||
}
|
||||
```
|
||||
|
||||
Since this method describes the view at any point in time, the value of the text input should *always* be `Untitled`.
|
||||
|
||||
|
||||
### Why Textarea Value?
|
||||
|
||||
In HTML, the value of `<textarea>` is usually set using its children:
|
||||
|
||||
```html
|
||||
<!-- counterexample: DO NOT DO THIS! -->
|
||||
<textarea name="description">This is the description.</textarea>
|
||||
```
|
||||
|
||||
For HTML, this easily allows developers to supply multiline values. However, since React is JavaScript, we do not have string limitations and can use `\n` if we want newlines. In a world where we have `value` and `defaultValue`, it is ambiguous what role children play. For this reason, you should not use children when setting `<textarea>` values:
|
||||
|
||||
```javascript
|
||||
<textarea name="description" value="This is a description." />
|
||||
```
|
||||
|
||||
If you *do* decide to use children, they will behave like `defaultValue`.
|
||||
132
docs/docs/07-working-with-the-browser.md
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
id: working-with-the-browser
|
||||
title: Working With the Browser
|
||||
layout: docs
|
||||
permalink: working-with-the-browser.html
|
||||
prev: forms.html
|
||||
next: more-about-refs.html
|
||||
---
|
||||
|
||||
React provides powerful abstractions that free you from touching the DOM directly in most cases, but sometimes you simply need to access the underlying API, perhaps to work with a third-party library or existing code.
|
||||
|
||||
|
||||
## The Mock DOM
|
||||
|
||||
React is so fast because it never talks to the DOM directly. React maintains a fast in-memory representation of the DOM. `render()` methods return a *description* of the DOM, and React can diff this description with the in-memory representation to compute the fastest way to update the browser.
|
||||
|
||||
Additionally, React implements a full synthetic event system such that all event objects are guaranteed to conform to the W3C spec despite browser quirks, and everything bubbles consistently and in a performant way cross-browser. You can even use some HTML5 events in IE8!
|
||||
|
||||
Most of the time you should stay within React's "faked browser" world since it's more performant and easier to reason about. However, sometimes you simply need to access the underlying API, perhaps to work with a third-party library like a jQuery plugin. React provides escape hatches for you to use the underlying DOM API directly.
|
||||
|
||||
|
||||
## Refs and getDOMNode()
|
||||
|
||||
To interact with the browser, you'll need a reference to a DOM node. Every mounted React component has a `getDOMNode()` function which you can call to get a reference to it.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> `getDOMNode()` only works on mounted components (that is, components that have been placed in the DOM). If you try to call this on a component that has not been mounted yet (like calling `getDOMNode()` in `render()` on a component that has yet to be created) an exception will be thrown.
|
||||
|
||||
In order to get a reference to a React component, you can either use `this` to get the current React component, or you can use refs to refer to a component you own. They work like this:
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
handleClick: function() {
|
||||
// Explicitly focus the text input using the raw DOM API.
|
||||
this.refs.myTextInput.getDOMNode().focus();
|
||||
},
|
||||
render: function() {
|
||||
// The ref attribute adds a reference to the component to
|
||||
// this.refs when the component is mounted.
|
||||
return (
|
||||
<div>
|
||||
<input type="text" ref="myTextInput" />
|
||||
<input
|
||||
type="button"
|
||||
value="Focus the text input"
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(
|
||||
<MyComponent />,
|
||||
document.getElementById('example')
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
## More About Refs
|
||||
|
||||
To learn more about refs, including ways to use them effectively, see our [more about refs](./more-about-refs.html) documentation.
|
||||
|
||||
|
||||
## Component Lifecycle
|
||||
|
||||
Components have three main parts of their lifecycle:
|
||||
|
||||
* **Mounting:** A component is being inserted into the DOM.
|
||||
* **Updating:** A component is being re-rendered to determine if the DOM should be updated.
|
||||
* **Unmounting:** A component is being removed from the DOM.
|
||||
|
||||
React provides lifecycle methods that you can specify to hook into this process. We provide **will** methods, which are called right before something happens, and **did** methods which are called right after something happens.
|
||||
|
||||
|
||||
### Mounting
|
||||
|
||||
* `getInitialState(): object` is invoked before a component is mounted. Stateful components should implement this and return the initial state data.
|
||||
* `componentWillMount()` is invoked immediately before mounting occurs.
|
||||
* `componentDidMount(DOMElement rootNode)` is invoked immediately after mounting occurs. Initialization that requires DOM nodes should go here.
|
||||
|
||||
|
||||
### Updating
|
||||
|
||||
* `componentWillReceiveProps(object nextProps)` is invoked when a mounted component receives new props. This method should be used to compare `this.props` and `nextProps` to perform state transitions using `this.setState()`.
|
||||
* `shouldComponentUpdate(object nextProps, object nextState): boolean` is invoked when a component decides whether any changes warrant an update to the DOM. Implement this as an optimization to compare `this.props` with `nextProps` and `this.state` with `nextState` and return false if React should skip updating.
|
||||
* `componentWillUpdate(object nextProps, object nextState)` is invoked immediately before updating occurs. You cannot call `this.setState()` here.
|
||||
* `componentDidUpdate(object prevProps, object prevState, DOMElement rootNode)` is invoked immediately after updating occurs.
|
||||
|
||||
|
||||
### Unmounting
|
||||
|
||||
* `componentWillUnmount()` is invoked immediately before a component is unmounted and destroyed. Cleanup should go here.
|
||||
|
||||
|
||||
### Mounted Methods
|
||||
|
||||
_Mounted_ composite components also support the following methods:
|
||||
|
||||
* `getDOMNode(): DOMElement` can be invoked on any mounted component in order to obtain a reference to its rendered DOM node.
|
||||
* `forceUpdate()` can be invoked on any mounted component when you know that some deeper aspect of the component's state has changed without using `this.setState()`.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> The `DOMElement rootNode` argument of `componentDidMount()` and
|
||||
> `componentDidUpdate()` is a convenience. The same node can be obtained by
|
||||
> calling `this.getDOMNode()`.
|
||||
|
||||
|
||||
## Browser Suppport and Polyfills
|
||||
|
||||
At Facebook, we support older browsers, including IE8. We've had polyfills in place for a long time to allow us to write forward-thinking JS. This means we don't have a bunch of hacks scattered throughout our codebase and we can still expect our code to "just work". For example, instead of seeing `+new Date()`, we can just write `Date.now()`. Since the open source React is the same as what we use internally, we've carried over this philosophy of using forward thinking JS.
|
||||
|
||||
In addition to that philosphy, we've also taken the stance that we, as authors of a JS library, should not be shipping polyfills as a part of our library. If every library did this, there's a good chance you'd be sending down the same polyfill multiple times, which could be a sizable chunk of dead code. If your product needs to support older browsers, chances are you're already using something like [es5-shim](https://github.com/kriskowal/es5-shim).
|
||||
|
||||
|
||||
### Polyfills Needed to Support Older Browsers
|
||||
|
||||
* `Array.isArray`
|
||||
* `Array.prototype.forEach`
|
||||
* `Array.prototype.indexOf`
|
||||
* `Function.prototype.bind`
|
||||
* `Date.now`
|
||||
* `Array.prototype.some` (also in `es5-shim.js`)
|
||||
|
||||
All of these can be polyfilled using `es5-shim.js` from [https://github.com/kriskowal/es5-shim](https://github.com/kriskowal/es5-shim).
|
||||
|
||||
* `console.*` - Only needed when not using the minified build. If you need to polyfill this, try [https://github.com/paulmillr/console-polyfill](https://github.com/paulmillr/console-polyfill).
|
||||
* `Object.create` - Provided in `es5-sham.js` @ [https://github.com/kriskowal/es5-shim](https://github.com/kriskowal/es5-shim).
|
||||
135
docs/docs/07.1-more-about-refs.md
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
id: more-about-refs
|
||||
title: More About Refs
|
||||
layout: docs
|
||||
permalink: more-about-refs.html
|
||||
prev: working-with-the-browser.html
|
||||
next: tooling-integration.html
|
||||
---
|
||||
After returning the structure of your UI from the render method, you may find yourself wanting to "reach out" and invoke methods on component instances returned from render. Often, doing something like this isn't necessary for making data flow through your application, because the Reactive data flow always ensures that the most recent `props` are sent to each child that is output from `render()`. However there are a few cases, where it still might be necessary or beneficial.
|
||||
|
||||
Consider the case when you wish to tell an `<input />` element (that exists within your instances sub-hierarchy) to focus after you update its value to be the empty string, `''`.
|
||||
|
||||
```javascript
|
||||
var App = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {userInput: ''};
|
||||
},
|
||||
handleKeyUp: function(e) {
|
||||
this.setState({userInput: e.target.value});
|
||||
},
|
||||
clearAndFocusInput: function() {
|
||||
this.setState({userInput: ''}); // Clear the input
|
||||
// We wish to focus the <input /> now!
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<div onClick={this.clearAndFocusInput}>
|
||||
Click To Focus and Reset
|
||||
</div>
|
||||
<input
|
||||
value={this.state.userInput}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
Notice how, in this example, we want to "tell" the input something - something that it cannot infer from it's props over time. In this case we want to "tell" it that it should now become focused. However, there are some challenges. What is returned from `render()`` is not your actual composition of "child" components, it is merely a *description* of the children at a particular instance in time - a snapshot, if you will.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> Remember, what you return from `render()` is not your *actual* rendered children instances. What you return from `render()` is merely a *description* of the children instances in your component's sub-hierarchy at a particular moment in time.
|
||||
|
||||
|
||||
This means that you should never "hold onto" something that you return from `render()` and then expect it to be anything meaningful.
|
||||
|
||||
```javascript
|
||||
// counterexample: DO NOT DO THIS!
|
||||
render: function() {
|
||||
var myInput = <input />; // I'm going to try to call methods on this
|
||||
this.rememberThisInput = myInput; // input at some point in the future! YAY!
|
||||
return (
|
||||
<div>
|
||||
<div>...</div>
|
||||
{myInput}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
In this counterexample, the `<input />` is merely a *description* of an `<input />`. This description is used to create a *real* **backing instance** for the `<input />`.
|
||||
|
||||
So how do we talk to the *real* backing instance of the input?
|
||||
|
||||
## The ref Attribute
|
||||
|
||||
React supports a very special property that you can attach to any component that is output from `render()`. This special property allows you to refer to the corresponding **backing instance** of anything returned from `render()`. It is always guaranteed to be the proper instance, at any point in time.
|
||||
|
||||
It's as simple as:
|
||||
|
||||
**1.** Assign a `ref` attribute to anything returned from `render` such as:
|
||||
|
||||
```html
|
||||
<input ref="myInput" />
|
||||
```
|
||||
|
||||
**2.** In some other code (typically event handler code), access the **backing instance** via `this.refs` as in:
|
||||
|
||||
```javascript
|
||||
this.refs.myInput
|
||||
```
|
||||
|
||||
## Completing the Example
|
||||
|
||||
```javascript
|
||||
var App = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {userInput: ''};
|
||||
},
|
||||
handleKeyUp: function(e) {
|
||||
this.setState({userInput: e.target.value});
|
||||
},
|
||||
clearAndFocusInput: function() {
|
||||
this.setState({userInput: ''}); // Clear the input
|
||||
this.refs.theInput.getDOMNode().focus(); // Boom! Focused!
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<div onClick={this.clearAndFocusInput}>
|
||||
Click To Focus and Reset
|
||||
</div>
|
||||
<input
|
||||
ref="theInput"
|
||||
value={this.state.userInput}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In this example, our render function returns a description of an `<input />` instance. But the true instance is accessed via `this.refs.theInput`. As long as a child component with `ref="theInput"` is returned from render, `this.refs.theInput` will access the the proper instance. This even works on higher level (non-DOM) components such as `<Typeahead ref="myTypeahead" />`.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
Refs are a great way to send a message to a particular child instance in a way that would be inconvenient to do via streaming Reactive `props` and `state`. They should, however, not be your go-to abstraction for flowing data through your application. By default, use the Reactive data flow and save `ref`s for use cases that are inherently non-reactive.
|
||||
|
||||
### Benefits:
|
||||
|
||||
- You can define any public method on your component classes (such as a reset method on a Typeahead) and call those public methods through refs (such as `this.refs.myTypeahead.reset()`).
|
||||
- Performing DOM measurements almost always requires reaching out to a "native" component such as `<input />` and accessing its underlying DOM node via `this.refs.myInput.getDOMNode()`. Refs are one of the only practical ways of doing this reliably.
|
||||
- Refs are automatically book-kept for you! If that child is destroyed, its ref is also destroyed for you. No worrying about memory here (unless you do something crazy to retain a reference yourself).
|
||||
|
||||
### Cautions:
|
||||
|
||||
- *Never* access refs inside of any component's render method - or while any component's render method is even running anywhere in the call stack.
|
||||
- If you want to preserve Google Closure Compiler Crushing resilience, make sure to never access as a property what was specified as a string. This means you must access using `this.refs['myRefString']` if your ref was defined as `ref="myRefString"`.
|
||||
- If you have not programmed several apps with React, your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where `state` should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use `ref`s to "make things happen" - instead, the data flow will usually accomplish your goal.
|
||||
44
docs/docs/08-tooling-integration.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
id: tooling-integration
|
||||
title: Tooling integration
|
||||
layout: docs
|
||||
permalink: tooling-integration.html
|
||||
prev: more-about-refs.html
|
||||
next: reference.html
|
||||
---
|
||||
|
||||
Every project uses a different system for building and deploying JavaScript. We've tried to make React as environment-agnostic as possible.
|
||||
|
||||
|
||||
## CDN-hosted React
|
||||
|
||||
We provide CDN-hosted versions of React [on our download page](/react/downloads.html). These prebuilt files use the UMD module format. Dropping them in with a simple `<script>` tag will inject a `React` global into your environment. It should also work out-of-the-box in CommonJS and AMD environments.
|
||||
|
||||
|
||||
## Using master
|
||||
|
||||
We have instructions for building from `master` [in our GitHub repository](https://github.com/facebook/react). We build a tree of CommonJS modules under `build/modules` which you can drop into any environment or packaging tool that supports CommonJS.
|
||||
|
||||
|
||||
## In-browser JSX Transform
|
||||
|
||||
If you like using JSX, we provide an in-browser JSX transformer for development [on our download page](/react/downloads.html). Simply include a `<script type="text/jsx">` tag to engage the JSX transformer. Be sure to include the `/** @jsx React.DOM */` comment as well, otherwise the transformer will not run the transforms.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> The in-browser JSX transformer is fairly large and results in extraneous computation client-side that can be avoided. Do not use it in production — see the next section.
|
||||
|
||||
|
||||
## Productionizing: Precompiled JSX
|
||||
|
||||
If you have [npm](http://npmjs.org/), you can simply run `npm install -g react-tools` to install our command-line `jsx` tool. This tool will translate files that use JSX syntax to plain JavaScript files that can run directly in the browser. It will also watch directories for you and automatically transform files when they are changed; for example: `jsx --watch src/ build/`. Run `jsx --help` for more information on how to use this tool.
|
||||
|
||||
|
||||
## Helpful Open-Source Projects
|
||||
|
||||
The open-source community has built tools that integrate JSX with several build systems.
|
||||
|
||||
* [reactify](https://github.com/andreypopp/reactify) - use JSX with [browserify](http://browserify.org/).
|
||||
* [grunt-react](https://github.com/ericclemmons/grunt-react) - [grunt](http://gruntjs.com/) task for JSX
|
||||
* [require-jsx](https://github.com/seiffert/require-jsx) - use JSX with [require.js](http://requirejs.org/)
|
||||
* [reactapp](https://github.com/jordwalke/reactapp) - a sample project to get up-and-running with React quickly
|
||||
209
docs/docs/09-reference.md
Normal file
@@ -0,0 +1,209 @@
|
||||
---
|
||||
id: reference
|
||||
title: Reference
|
||||
layout: docs
|
||||
permalink: reference.html
|
||||
prev: tooling-integration.html
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
### Production Apps
|
||||
|
||||
* All of [Instagram.com](http://instagram.com/) is built on React.
|
||||
* Many components on [Facebook.com](http://www.facebook.com/), including the commenting interface, ads creation flows, and page insights.
|
||||
* [Khan Academy](http://khanacademy.org/) is using React for its question editor.
|
||||
|
||||
|
||||
### Sample Code
|
||||
|
||||
* We've included [a step-by-step comment box tutorial](./tutorial.html).
|
||||
* [The React starter kit](/react/downloads.html) includes several examples which you can [view online in our GitHub repository](https://github.com/facebook/react/tree/master/examples/).
|
||||
* [reactapp](https://github.com/jordwalke/reactapp) is a simple app template to get you up-and-running quickly with React.
|
||||
* [React one-hour email](https://github.com/petehunt/react-one-hour-email/commits/master) goes step-by-step from a static HTML mock to an interactive email reader (written in just one hour!)
|
||||
* [Rendr + React app template](https://github.com/petehunt/rendr-react-template/) demonstrates how to use React's server rendering capabilities.
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### React
|
||||
|
||||
`React` is the entry point to the React framework. If you're using one of the prebuilt packages it's available as a global; if you're using CommonJS modules you can `require()` it.
|
||||
|
||||
|
||||
#### React.DOM
|
||||
|
||||
`React.DOM` provides all of the standard HTML tags needed to build a React app. You generally don't use it directly; instead, just include it as part of the `/** @jsx React.DOM */` docblock.
|
||||
|
||||
|
||||
#### React.initializeTouchEvents
|
||||
|
||||
```javascript
|
||||
initializeTouchEvents(boolean shouldUseTouch)
|
||||
```
|
||||
|
||||
Configure React's event system to handle touch events on mobile devices.
|
||||
|
||||
|
||||
#### React.createClass
|
||||
|
||||
```javascript
|
||||
function createClass(object specification)
|
||||
```
|
||||
|
||||
Creates a component given a specification. A component implements a `render` method which returns **one single** child. That child may have an arbitrarily deep child structure. One thing that makes components different than a standard prototypal classes is that you don't need to call new on them. They are convenience wrappers that construct backing instances (via new) for you.
|
||||
|
||||
|
||||
#### React.renderComponent
|
||||
|
||||
```javascript
|
||||
ReactComponent renderComponent(ReactComponent container, DOMElement container)
|
||||
```
|
||||
|
||||
Renders a React component into the DOM in the supplied `container`.
|
||||
|
||||
If the React component was previously rendered into `container`, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React component.
|
||||
|
||||
|
||||
#### React.unmountAndReleaseReactRootNode
|
||||
|
||||
```javascript
|
||||
unmountAndReleaseReactRootNode(DOMElement container)
|
||||
```
|
||||
|
||||
Remove a mounted React component from the DOM and clean up its event handlers and state.
|
||||
|
||||
|
||||
#### React.renderComponentToString
|
||||
|
||||
```javascript
|
||||
renderComponentToString(ReactComponent component, function callback)
|
||||
```
|
||||
|
||||
Render a component to its initial HTML. This should only be used on the server. React will call `callback` with an HTML string when the markup is ready. You can use this method to create static site generators, or you can generate HTML on the server and send it down to have a very fast initial page load. If you call `React.renderComponent()` on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.
|
||||
|
||||
|
||||
### AbstractEvent
|
||||
|
||||
Your event handlers will be passed instances of `AbstractEvent`, a cross-browser wrapper around the browser's native event. It has the same interface as the browser's native event (such as `stopPropagation()` and `preventDefault()`) except they work exactly the same across all browsers.
|
||||
|
||||
If you find that you need the underlying browser event for some reason, simply use the `nativeEvent` attribute to get it.
|
||||
|
||||
|
||||
### ReactComponent
|
||||
|
||||
Component classses created by `createClass()` return instances of `ReactComponent` when called. Most of the time when you're using React you're either creating or consuming `ReactComponent`s.
|
||||
|
||||
|
||||
#### getDOMNode
|
||||
|
||||
```javascript
|
||||
DOMElement getDOMNode()
|
||||
```
|
||||
|
||||
If this component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements.
|
||||
|
||||
|
||||
#### setProps
|
||||
|
||||
```javascript
|
||||
setProps(object nextProps)
|
||||
```
|
||||
|
||||
When you're integrating with an external JavaScript application you may want to signal a change to a React component rendered with `renderComponent()`. Simply call `setProps()` to change its properties and trigger a re-render.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> This method can only be called on a root-level component. That is, it's only available on the component passed directly to `renderComponent()` and none of its children. If you're inclined to use `setProps()` on a child component, instead take advantage of reactive updates and pass the new prop to the child component when it's created in `render()`.
|
||||
|
||||
|
||||
#### replaceProps
|
||||
|
||||
```javascript
|
||||
replaceProps(object nextProps)
|
||||
```
|
||||
|
||||
Like `setProps()` but deletes any pre-existing props that are not in nextProps.
|
||||
|
||||
|
||||
#### transferPropsTo
|
||||
|
||||
```javascript
|
||||
ReactComponent transferPropsTo(ReactComponent targetComponent)
|
||||
```
|
||||
|
||||
Transfer properties from this component to a target component that have not already been set on the target component. This is usually used to pass down properties to the returned root component. `targetComponent`, now updated with some new props is returned as a convenience.
|
||||
|
||||
|
||||
#### setState
|
||||
|
||||
```javascript
|
||||
setState(object nextState[, function callback])
|
||||
```
|
||||
|
||||
Merges nextState with the current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. In addition, you can supply an optional callback function that is executed once `setState` is completed.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> *NEVER* mutate `this.state` directly. As calling `setState()` afterwards may replace the mutation you made. Treat `this.state` as if it were immutable.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> `setState()` does not immediately mutate `this.state` but creates a pending state transition. Accessing `this.state` after calling this method can potentially return the existing value.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> There is no guarantee of synchronous operation of calls to `setState` and calls may be batched for performance gains.
|
||||
|
||||
|
||||
#### replaceState
|
||||
|
||||
```javascript
|
||||
replaceState(object nextState[, function callback])
|
||||
```
|
||||
|
||||
Like `setState()` but deletes any pre-existing state keys that are not in nextState.
|
||||
|
||||
|
||||
#### forceUpdate()
|
||||
|
||||
```javascript
|
||||
forceUpdate([function callback])
|
||||
```
|
||||
|
||||
If your `render()` method reads from something other than `this.props` or `this.state` you'll need to tell React when it needs to re-run `render()`. Use `forceUpdate()` to cause React to automatically re-render. This will cause `render()` to be called on the component and all of its children but React will only update the DOM if the markup changes.
|
||||
|
||||
Normally you should try to avoid all uses of `forceUpdate()` and only read from `this.props` and `this.state` in `render()`. This makes your application much simpler and more efficient.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> There is no guarantee of synchronous operation of calls to `forceUpdate` and calls may be batched for performance gains.
|
||||
|
||||
|
||||
#### Lifecycle methods
|
||||
|
||||
```javascript
|
||||
object getInitialState()
|
||||
componentWillMount()
|
||||
componentDidMount(DOMElement domNode)
|
||||
componentWillReceiveProps(object nextProps)
|
||||
boolean shouldComponentUpdate(object nextProps, object nextState)
|
||||
componentWillUpdate(object nextProps, object nextState)
|
||||
ReactComponent render()
|
||||
componentDidUpdate(object prevProps, object prevState, DOMElement domNode)
|
||||
componentWillUnmount()
|
||||
```
|
||||
|
||||
See the [working with the browser](./working-with-the-browser.html) documentation for more details on these lifecycle methods.
|
||||
|
||||
|
||||
## DOM Differences
|
||||
|
||||
React has implemented a browser-independent events and DOM system for performance and cross-browser compatibility reasons. We took the opportunity to clean up a few rough edges in browser DOM implementations.
|
||||
|
||||
* All events (including submit) bubble correctly per the W3C spec
|
||||
* All event objects conform to the W3C spec
|
||||
* All DOM properties and attributes (including event handlers) should be camelCased to be consistent with standard JavaScript style. We intentionally break with the spec here, since the spec is inconsistent.
|
||||
* The `style` attribute accepts a JavaScript object with camelCased properties rather than a CSS string. This is consistent with the DOM `style` JavaScript property, is more efficient, and prevents XSS security holes.
|
||||
* `onChange` behaves as you would expect it to: whenever a form field is changed this event is fired rather than inconsistently on blur. We intentionally break from existing browser behavior because `onChange` is a misnomer for its behavior and React relies on this event to react to user input in real time.
|
||||
96
docs/docs/OUTLINE.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
id: OUTLINE
|
||||
title: Goals of the documentation
|
||||
layout: docs
|
||||
prev: 09.1-tutorial.html
|
||||
---
|
||||
- Flow of docs should mimic progression of questions a new user would ask
|
||||
- High information density -- assume the reader is adept at JS
|
||||
- Talk about best practices
|
||||
- JSFiddles for all code samples
|
||||
- Provide background for some of the design decisions
|
||||
- Less words less words less words!
|
||||
|
||||
## Outline
|
||||
|
||||
Motivation / Why React?
|
||||
- Declarative (simple)
|
||||
- Components (separation of concerns)
|
||||
- Give it 5 minutes
|
||||
|
||||
Displaying data
|
||||
- Hello world example
|
||||
- Reactive updates
|
||||
- Components are just functions
|
||||
- JSX syntax (link to separate doc?)
|
||||
- JSX gotchas
|
||||
|
||||
Interactivity and dynamic UIs
|
||||
- Click handler example
|
||||
- Event handlers / synthetic events (link to w3c docs)
|
||||
- Under the hood: autoBind and event delegation (IE8 notes)
|
||||
- React is a state machine
|
||||
- How state works
|
||||
- What components should have state?
|
||||
- What should go in state?
|
||||
- What shouldn't go in state?
|
||||
|
||||
Scaling up: using multiple components
|
||||
- Motivation: separate concerns
|
||||
- Composition example
|
||||
- Ownership (and owner vs. parent)
|
||||
- Children
|
||||
- Data flow (one-way data binding)
|
||||
- A note on performance
|
||||
|
||||
Building effective reusable components
|
||||
- You should build a reusable component library (CSS, testing etc)
|
||||
- Prop validation
|
||||
- Transferring props: a shortcut
|
||||
- Mixins
|
||||
- Testing
|
||||
|
||||
Forms
|
||||
|
||||
Working with the browser
|
||||
- The mock DOM
|
||||
- Refs / getDOMNode()
|
||||
- More about refs
|
||||
- Component lifecycle
|
||||
- Browser support and polyfills
|
||||
|
||||
Working with your environment
|
||||
- CDN-hosted React
|
||||
- Using master
|
||||
- In-browser JSX transform
|
||||
- Productionizing: precompiled JSX
|
||||
- Helpful open-source projects
|
||||
|
||||
Integrating with other UI libraries
|
||||
- Using jQuery plugins
|
||||
- Letting jQuery manage React components
|
||||
- Using with Backbone.View
|
||||
- CoffeeScript
|
||||
- Moving from Handlebars to React: an example
|
||||
|
||||
Server / static rendering
|
||||
- Motivation
|
||||
- Simple example
|
||||
- How does it work? (No DOM)
|
||||
- Rendr + React
|
||||
|
||||
Big ideas
|
||||
- Animation
|
||||
- Bootstrap bindings (responsive grids)
|
||||
- Reactive CSS
|
||||
- Web workers
|
||||
- Native views
|
||||
|
||||
Case studies
|
||||
- Comment box tutorial from scratch
|
||||
- From HTML mock to application: React one-hour email
|
||||
- Jordan's LikeToggler example
|
||||
|
||||
Reference
|
||||
- API
|
||||
- DOM differences
|
||||
@@ -1,119 +0,0 @@
|
||||
---
|
||||
id: docs-advanced-components
|
||||
title: Advanced Components
|
||||
description: How to build advanced composite components.
|
||||
layout: docs
|
||||
prev: event-handling.html
|
||||
next: api.html
|
||||
---
|
||||
|
||||
Composite components extend a `ReactCompositeComponent` base class that provides
|
||||
a very powerful API that makes React flexible and able to easily work with other
|
||||
libraries and frameworks.
|
||||
|
||||
## Lifecycle Methods
|
||||
|
||||
Composite components can optionally implement lifecycle methods that are invoked
|
||||
at various stages in the [component lifecycle](component-lifecycle.html) that
|
||||
each have unique guarantees.
|
||||
|
||||
### Mounting
|
||||
|
||||
- `getInitialState(): object` is invoked before a component is mounted.
|
||||
Stateful components should implement this and return the initial state data.
|
||||
- `componentWillMount()` is invoked immediately before mounting occurs.
|
||||
- `componentDidMount(DOMElement rootNode)` is invoked immediately after
|
||||
mounting occurs. Initialization that requires DOM nodes should go here.
|
||||
|
||||
### Updating
|
||||
|
||||
- `componentWillReceiveProps(object nextProps)` is invoked when a mounted
|
||||
component receives new props. This method should be used to compare
|
||||
`this.props` and `nextProps` to perform state transitions using
|
||||
`this.setState()`.
|
||||
- `shouldComponentUpdate(object nextProps, object nextState): boolean` is
|
||||
invoked when a component decides whether any changes warrant an update to the
|
||||
DOM. Implement this as an optimization to compare `this.props` with
|
||||
`nextProps` and `this.state` with `nextState` and return false if React
|
||||
should skip updating.
|
||||
- `componentWillUpdate(object nextProps, object nextState)` is invoked
|
||||
immediately before updating occurs. You cannot call `this.setState()` here.
|
||||
- `componentDidUpdate(object prevProps, object prevState, DOMElement rootNode)`
|
||||
is invoked immediately after updating occurs.
|
||||
|
||||
### Unmounting
|
||||
|
||||
- `componentWillUnmount()` is invoked immediately before a component is
|
||||
unmounted and destroyed. Cleanup should go here.
|
||||
|
||||
## Mounted Methods
|
||||
|
||||
_Mounted_ composite components also support the following methods:
|
||||
|
||||
- `getDOMNode(): DOMElement` can be invoked on any mounted component in order
|
||||
to obtain a reference to its rendered DOM node.
|
||||
- `forceUpdate()` can be invoked on any mounted component when you know that
|
||||
some deeper aspect of the component's state has changed without using
|
||||
`this.setState()`.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> The `DOMElement rootNode` argument of `componentDidMount()` and
|
||||
> `componentDidUpdate()` is a convenience. The same node can be obtained by
|
||||
> calling `this.getDOMNode()`.
|
||||
|
||||
## Component Refs
|
||||
|
||||
A common use case of event callbacks or the lifecycle methods is to operate on a
|
||||
component returned by `render()`. For example, consider a search component that
|
||||
should auto-focus the input once mounted:
|
||||
|
||||
```javascript
|
||||
var SearchForm = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<form action={this.props.action}>
|
||||
<input type="search" placeholder="Search..." />
|
||||
</form>
|
||||
);
|
||||
},
|
||||
componentDidMount: function(rootNode) {
|
||||
var searchInput = rootNode.firstChild;
|
||||
searchInput.focus();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Although this implementation works, it is fragile because `componentDidMount()`
|
||||
now relies on `render()` returning a particular DOM structure.
|
||||
|
||||
React provides a better way for composite components to reference components
|
||||
that it constructs in its `render()` method through the use of refs. A component
|
||||
can assign a `ref` to any component it constructs. This will create a reference
|
||||
to those components on `this.refs`. For example:
|
||||
|
||||
```javascript{5,10}
|
||||
var SearchForm = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<form action={this.props.action}>
|
||||
<input type="search" placeholder="Search..." ref="searchInput" />
|
||||
</form>
|
||||
);
|
||||
},
|
||||
componentDidMount: function(rootNode) {
|
||||
var searchInput = this.refs.searchInput.getDOMNode();
|
||||
searchInput.focus();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In this example, `this.refs.searchInput` will reference the `<input>` component
|
||||
and is available in most lifecycle methods and event callbacks. We obtain a
|
||||
reference to the `<input>`'s DOM node using `getDOMNode()`.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> If you want to preserve compatibility with Google Closure Compiler's
|
||||
> property crushing in `ADVANCED_OPTIMIZATIONS` mode, make sure to use string
|
||||
> literals with `this.refs`.
|
||||
150
docs/docs/api.md
@@ -1,150 +0,0 @@
|
||||
---
|
||||
id: docs-api
|
||||
title: React API
|
||||
layout: docs
|
||||
prev: advanced-components.html
|
||||
---
|
||||
|
||||
## React
|
||||
|
||||
`React` is the entry point to the React framework. If you're using one of the prebuilt packages it's available as a global; if you're using CommonJS modules you can `require()` it.
|
||||
|
||||
#### React.DOM
|
||||
|
||||
`React.DOM` provides all of the standard HTML tags needed to build a React app. You generally don't use it directly; instead, just include it as part of the `/** @jsx React.DOM */` docblock.
|
||||
|
||||
#### React.initializeTouchEvents
|
||||
|
||||
```javascript
|
||||
initializeTouchEvents(boolean shouldUseTouch)
|
||||
```
|
||||
|
||||
Configure React's event system to handle touch events on mobile devices.
|
||||
|
||||
#### React.autoBind
|
||||
|
||||
```javascript
|
||||
function autoBind(function method)
|
||||
```
|
||||
|
||||
Marks the provided function to be automatically bound to each React component instance created. This allows React components to define automatically bound methods and ensure that when called they will always reference their current instance.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
React.createClass({
|
||||
click: React.autoBind(function(evt) {
|
||||
this.setState({jumping: true});
|
||||
}),
|
||||
render: function() {
|
||||
// Look: no bind!
|
||||
return <a onClick={this.click}>Jump</a>;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### React.createClass
|
||||
|
||||
```javascript
|
||||
function createClass(object specification)
|
||||
```
|
||||
|
||||
Creates a component given a specification. A component implements a `render` method which returns a single child. That child may have an arbitrarily deep child structure. One thing that makes components different than a standard prototypal classes is that you don't need to call new on them. They are convenience wrappers that construct backing instances (via new) for you.
|
||||
|
||||
#### React.renderComponent
|
||||
|
||||
```javascript
|
||||
ReactComponent renderComponent(ReactComponent container, DOMElement mountPoint)
|
||||
```
|
||||
|
||||
Renders a React component into the DOM in the supplied `container`.
|
||||
|
||||
If the React component was previously rendered into `container`, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React component.
|
||||
|
||||
## AbstractEvent
|
||||
|
||||
Your event handlers will be passed instances of `AbstractEvent`, a cross-browser wrapper around the browser's native event. It has the same interface as the browser's native event (such as `stopPropagation()` and `preventDefault()`) except they work exactly the same across all browsers.
|
||||
|
||||
If you find that you need the underlying browser event for some reason, simply use the `nativeEvent` attribute to get it.
|
||||
|
||||
## ReactComponent
|
||||
|
||||
Component classses created by `createClass()` return instances of `ReactComponent` when called. Most of the time when you're using React you're either creating or consuming `ReactComponent`s.
|
||||
|
||||
#### getDOMNode
|
||||
|
||||
```javascript
|
||||
DOMElement getDOMNode()
|
||||
```
|
||||
|
||||
If this component has been mounted into the DOM, this returns the corresponding native browser DOM element. This method is useful for reading values out of the DOM, such as form field values and performing DOM measurements.
|
||||
|
||||
#### setProps
|
||||
|
||||
```javascript
|
||||
setProps(object nextProps)
|
||||
```
|
||||
|
||||
When you're integrating with an external JavaScript application you may want to signal a change to a React component rendered with `renderComponent()`. Simply call `setProps()` to change its properties and trigger a re-render.
|
||||
|
||||
**Note:** This method can only be called on a root-level component. That is, it's only available on the component passed directly to `renderComponent()` and none of its children. If you're inclined to use `setProps()` on a child component, instead take advantage of reactive updates and pass the new prop to the child component when it's created in `render()`.
|
||||
|
||||
#### replaceProps
|
||||
|
||||
```javascript
|
||||
replaceProps(object nextProps)
|
||||
```
|
||||
|
||||
Like `setProps()` but deletes any pre-existing props that are not in nextProps.
|
||||
|
||||
#### transferPropsTo
|
||||
|
||||
```javascript
|
||||
ReactComponent transferPropsTo(ReactComponent targetComponent)
|
||||
```
|
||||
|
||||
Transfer properties from this component to a target component that have not already been set on the target component. This is usually used to pass down properties to the returned root component. `targetComponent`, now updated with some new props is returned as a convenience.
|
||||
|
||||
#### setState
|
||||
|
||||
```javascript
|
||||
setState(object nextState)
|
||||
```
|
||||
|
||||
Merges nextState with the current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks.
|
||||
|
||||
**Note:** *NEVER* mutate `this.state` directly. As calling `setState()` afterwards may replace the mutation you made. Treat `this.state` as if it were immutable.
|
||||
|
||||
**Note:** `setState()` does not immediately mutate `this.state` but creates a pending state transition. Accessing `this.state` after calling this method can potentially return the existing value.
|
||||
|
||||
#### replaceState
|
||||
|
||||
```javascript
|
||||
replaceState(object nextState)
|
||||
```
|
||||
|
||||
Like `setState()` but deletes any pre-existing state keys that are not in nextState.
|
||||
|
||||
#### forceUpdate()
|
||||
|
||||
```javascript
|
||||
forceUpdate()
|
||||
```
|
||||
|
||||
If your `render()` method reads from something other than `this.props` or `this.state` you'll need to tell React when it needs to re-run `render()`. Use `forceUpdate()` to cause React to automatically re-render. This will cause `render()` to be called on the component and all of its children but React will only update the DOM if the markup changes.
|
||||
|
||||
Normally you should try to avoid all uses of `forceUpdate()` and only read from `this.props` and `this.state` in `render()`. This makes your application much simpler and more efficient.
|
||||
|
||||
```javascript
|
||||
object getInitialState()
|
||||
componentWillMount()
|
||||
componentDidMount(DOMElement domNode)
|
||||
componentWillReceiveProps(object nextProps)
|
||||
boolean shouldComponentUpdate(object nextProps, object nextState)
|
||||
componentWillUpdate(object nextProps, object nextState)
|
||||
ReactComponent render()
|
||||
componentDidUpdate(object prevProps, object prevState, DOMElement domNode)
|
||||
componentWillUnmount()
|
||||
```
|
||||
|
||||
See the [advanced components](advanced-components.html) documentation for more details on these lifecycle methods.
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
id: docs-common-questions
|
||||
title: Common Questions
|
||||
layout: docs
|
||||
prev: tutorial.html
|
||||
next: syntax.html
|
||||
---
|
||||
### What browsers does React support?
|
||||
|
||||
React supports the latest two Chrome, Firefox, Safari, and Internet Explorer versions. React can work with Internet Explorer 8 with polyfills.
|
||||
|
||||
### How do I get React to support Internet Explorer 8?
|
||||
|
||||
React requires ES5 JavaScript shims to run in Internet Explorer 8. Include the [ES5 Shims](https://github.com/kriskowal/es5-shim) to implement these shims.
|
||||
|
||||
### Who uses React?
|
||||
|
||||
The [Instagram](http://instagram.com/) website is built entirely in React. The [Facebook](https://www.facebook.com/) website is also increasingly using React, including the common commenting plugin across the site.
|
||||
|
||||
### Can I integrate with other JavaScript libraries?
|
||||
|
||||
Absolutely! In fact, we encourage it! See [our GitHub repo](http://github.com/facebook/react/) for a [TodoMVC example using Backbone](https://github.com/facebook/react/tree/master/examples/todomvc-backbone) and a [jQuery + Bootstrap modal demo](https://github.com/facebook/react/tree/master/examples/jquery-bootstrap).
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
id: docs-component-basics
|
||||
title: Component Basics
|
||||
description: What are components?
|
||||
layout: docs
|
||||
next: component-data.html
|
||||
prev: syntax.html
|
||||
---
|
||||
|
||||
_Components_ are the basic units of composition in React. Components encapsulate
|
||||
the logic necessary to take input parameters and render markup. Components can
|
||||
be rendered into an existing DOM element on the page by using
|
||||
`React.renderComponent`:
|
||||
|
||||
```javascript
|
||||
// Replaces everything in `document.body` with <div>Hello, world!</div>;
|
||||
React.renderComponent(<div>Hello, world!</div>, document.body);
|
||||
```
|
||||
|
||||
Keep in mind that `<div>` is **not** a DOM element! Keep reading...
|
||||
|
||||
## Types of Components
|
||||
|
||||
There are two types of components:
|
||||
|
||||
- **Composite Components**
|
||||
- **DOM Components**
|
||||
|
||||
### Composite Components <small>such as `TodoApp` and `Typeahead`.</small>
|
||||
|
||||
The majority of your React code will be implementing composite components.
|
||||
|
||||
Composite components are higher-level components with custom rendering logic
|
||||
that may compose other composite components or DOM components.
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
var LinkButton = React.createClass({
|
||||
render: function() {
|
||||
return <a className="btn" />;
|
||||
}
|
||||
});
|
||||
|
||||
var myButton = <LinkButton />;
|
||||
```
|
||||
|
||||
This example defines a `LinkButton` component class using `React.createClass()`,
|
||||
and its `render()` method composes the `<a>` DOM component.
|
||||
|
||||
### DOM Components <small>such as `div` and `span`.</small>
|
||||
|
||||
DOM components are the set of classes that correspond to browser DOM elements.
|
||||
They are defined in `React.DOM` and can be brought "into scope" by setting
|
||||
`@jsx React.DOM` in the docblock. See [JSX Syntax](syntax.html) for more
|
||||
details.
|
||||
|
||||
Although `React.DOM` components look like browser DOM elements, they differ in a
|
||||
few ways:
|
||||
|
||||
- All property names, including event handlers, are camelCased.
|
||||
- JavaScript identifiers should be used, namely `className` and `htmlFor`.
|
||||
- The `style` prop expects an object instead of a string. The object should map
|
||||
camelCased style properties to values, e.g. `{backgroundColor: '#fff'}`.
|
||||
|
||||
Here is an example of a React link styled as a button with a click handler:
|
||||
|
||||
```javascript
|
||||
/** @jsx React.DOM */
|
||||
var handleClick = function() {alert('Clicked!');};
|
||||
var inlineStyle = {textDecoration: 'none'};
|
||||
|
||||
var myLink = <a className="btn" onClick={handleClick} style={inlineStyle} />;
|
||||
```
|
||||
@@ -1,145 +0,0 @@
|
||||
---
|
||||
id: docs-component-data
|
||||
title: Component Data
|
||||
description: How is data passed into a component?
|
||||
layout: docs
|
||||
prev: component-basics.html
|
||||
next: component-lifecycle.html
|
||||
---
|
||||
|
||||
## Props
|
||||
|
||||
Components use data to determine what should be rendered. For example:
|
||||
|
||||
```javascript
|
||||
var LikeLink = React.createClass({
|
||||
render: function() {
|
||||
var text = this.props.liked ? 'Liked' : 'Like';
|
||||
return <a>{text}</a>;
|
||||
}
|
||||
});
|
||||
var myLikeLink = <LikeLink liked={false} />;
|
||||
```
|
||||
|
||||
In this example, `LikeLink` takes `liked` as boolean data. This type of data
|
||||
that is passed in is called a "prop". Examples of props on DOM components
|
||||
include `className` and `onClick`.
|
||||
|
||||
Whenever a component's props change, its `render()` function will be
|
||||
re-evaluated and the DOM will be updated. React will ensure that the DOM is
|
||||
always kept up-to-date.
|
||||
|
||||
## State
|
||||
|
||||
Let's build a small `LikeApp` application that makes use of the `<LikeLink>`
|
||||
component from above. It should start off unliked and we should be able to like
|
||||
it by clicking the link:
|
||||
|
||||
```javascript
|
||||
var LikeApp = React.createClass({
|
||||
render: function() {
|
||||
var isClicked = false;
|
||||
return <LikeLink liked={isClicked} onClick={this.handleClick.bind(this)} />;
|
||||
},
|
||||
handleClick: function() {
|
||||
// Somehow update `isClicked`.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This renders a `<LikeLink>` with a click listener. However, it is not clear how
|
||||
`handleClick` should update `isClicked` to true. `LikeApp` needs a way to store
|
||||
**state** about whether or not it has been clicked.
|
||||
|
||||
### State vs. Props
|
||||
|
||||
State is data that is managed _internally_ by a composite component. Like props,
|
||||
the `render()` function will be re-evaluated whenever state changes. Props and
|
||||
state differ in that:
|
||||
|
||||
- Props are passed in from the creator.
|
||||
- State is private to and managed by the component.
|
||||
|
||||
### Managing State
|
||||
|
||||
Let's update our `LikeApp` component using state:
|
||||
|
||||
```javascript{2-4,6,10}
|
||||
var LikeApp = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {isClicked: false};
|
||||
},
|
||||
render: function() {
|
||||
var isClicked = this.state.isClicked;
|
||||
return <LikeLink liked={isClicked} onClick={this.handleClick.bind(this)} />;
|
||||
},
|
||||
handleClick: function() {
|
||||
this.setState({isClicked: true});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
There's a lot going on here, so let's work our way from top to bottom:
|
||||
|
||||
- `getInitialState()` describes what state data looks like when the component
|
||||
is created.
|
||||
- In `render()`, state data can be accessed via `this.state`.
|
||||
- When the link is clicked, we update state using `setState()`.
|
||||
|
||||
Now when we click the link, the `<LikeLink>` will get updated, right? Wrong.
|
||||
|
||||
## Transferring Props
|
||||
|
||||
If you have been following carefully, you may have noticed that although we pass
|
||||
a click handler into `<LikeLink>` as a prop, `LikeLink` does not do anything
|
||||
with `this.props.onClick`! Let's fix that.
|
||||
|
||||
```javascript{4}
|
||||
var LikeLink = React.createClass({
|
||||
render: function() {
|
||||
var text = this.props.liked ? 'Liked' : 'Like';
|
||||
return <a onClick={this.props.onClick}>{text}</a>;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Although this works, realize that this would quickly become tedious if we wanted
|
||||
to also transfer `href`, `title`, `target`, and other events from `this` to the
|
||||
rendered `<a>`. React provides a convenience method, `transferPropsTo()`, for
|
||||
transferring props:
|
||||
|
||||
```javascript{4}
|
||||
var LikeLink = React.createClass({
|
||||
render: function() {
|
||||
var text = this.props.liked ? 'Liked' : 'Like';
|
||||
return this.transferPropsTo(<a>{text}</a>);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This will transfer all props from `this` to the specified component (including
|
||||
`onClick`).
|
||||
|
||||
## Summary
|
||||
|
||||
Now we are done. `LikeApp` renders an unliked link which, when clicked, will:
|
||||
|
||||
1. Update the internal state of `LikeApp`.
|
||||
2. Change the props passed into `LikeLink`.
|
||||
3. Change the return value of `render()`.
|
||||
4. Trigger an update to the DOM.
|
||||
|
||||
It's worth noting that React will handle new return values of `render()` by
|
||||
making the mininal set of mutations necessary to bring the DOM up-to-date. In
|
||||
this case, only the `textContent` of the rendered link will be mutated.
|
||||
|
||||
In summary:
|
||||
|
||||
- Props are passed in whereas state is managed internally by a component.
|
||||
- Never mutate `this.props` or `this.state`. You should pass props into other
|
||||
components and mutate state using `setState()`.
|
||||
- State is private. Never read `state` or call `setState()` on
|
||||
anything but `this`.
|
||||
- Whenever props or state changes, `render()` will be re-evaluated and the DOM
|
||||
updated. Also, `render()` should not depend on anything besides `this.props`
|
||||
and `this.state`.
|
||||
@@ -1,85 +0,0 @@
|
||||
---
|
||||
id: docs-component-lifecycle
|
||||
title: Component Lifecycle
|
||||
description: What happens when I render a React component?
|
||||
layout: docs
|
||||
prev: component-data.html
|
||||
next: event-handling.html
|
||||
---
|
||||
|
||||
## Mounting
|
||||
|
||||
[We have previously seen](component-basics.html) how to render components into
|
||||
existing DOM elements on the page:
|
||||
|
||||
```javascript
|
||||
React.renderComponent(<div>Hello, world!</div>, document.body);
|
||||
```
|
||||
|
||||
In this one simple line, we have accomplished the following:
|
||||
|
||||
- A `<div>` (defined by `React.DOM.div`) component is instantiated.
|
||||
- The component is **mounted** into `document.body`.
|
||||
|
||||
**Mounting** is the process of initializing a React component by creating its
|
||||
DOM nodes and inserting the them into a supplied container node.
|
||||
|
||||
At this point, the entire page consists of a single `<div>` with "Hello,
|
||||
world!".
|
||||
|
||||
## Updating
|
||||
|
||||
Let's add a second call to `React.renderComponent()` after three
|
||||
seconds:
|
||||
|
||||
```javascript{2-4}
|
||||
React.renderComponent(<div>Hello, world!</div>, document.body);
|
||||
setTimeout(function() {
|
||||
React.renderComponent(<div>Goodbye, world.</div>, document.body);
|
||||
}, 3000);
|
||||
```
|
||||
|
||||
The second call to `React.renderComponent()` will trigger the following:
|
||||
|
||||
- The `<div>` component will check the new props to see if anything changed.
|
||||
- The set of changes are used to **update** the DOM node as necessary.
|
||||
|
||||
**Updating** is the process of mutating the rendered DOM nodes and occurs
|
||||
whenever either props or state has changed. This ensures that the rendered DOM
|
||||
is consistent with the data.
|
||||
|
||||
## Unmounting
|
||||
|
||||
Let's add one final call to `React.renderComponent()` after another three
|
||||
seconds:
|
||||
|
||||
```javascript{5-7}
|
||||
React.renderComponent(<div>Hello, world!</div>, document.body);
|
||||
setTimeout(function() {
|
||||
React.renderComponent(<div>Goodbye, world.</div>, document.body);
|
||||
}, 3000);
|
||||
setTimeout(function() {
|
||||
React.renderComponent(<img src="/images/fin.png" />, document.body);
|
||||
}, 6000);
|
||||
```
|
||||
|
||||
The third call to `React.renderComponent()` will trigger the following:
|
||||
|
||||
- An `<img>` (defined by `React.DOM.img`) component is instantiated.
|
||||
- React will compare the `<div>` component with the `<img>` component.
|
||||
- Since the component class is different, the `<div>` component will be
|
||||
**unmounted**.
|
||||
- The `<img>` component will then be mounted into `document.body`.
|
||||
|
||||
**Unmounting** is the process of releasing resources that have been allocated by
|
||||
a component. This allows user interfaces built with React to live long without
|
||||
memory leaks.
|
||||
|
||||
Components can also be unmounted using
|
||||
`React.unmountAndReleaseReactRootNode()`:
|
||||
|
||||
```javascript
|
||||
React.unmountAndReleaseReactRootNode(document.body);
|
||||
```
|
||||
|
||||
This will unmount any components mounted immediately within `document.body`.
|
||||
@@ -1,224 +0,0 @@
|
||||
---
|
||||
id: docs-event-handling
|
||||
title: Event Handling
|
||||
description: How do events work with React components?
|
||||
layout: docs
|
||||
prev: component-lifecycle.html
|
||||
next: advanced-components.html
|
||||
---
|
||||
|
||||
Events in React work the way they do with HTML, except the event names are
|
||||
camelCased.
|
||||
|
||||
```javascript
|
||||
var Clicker = React.createClass({
|
||||
render: function() {
|
||||
return <span onClick={this.handleClick}>Click me!</span>;
|
||||
},
|
||||
handleClick: function(event) {
|
||||
alert('You clicked me!');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
When `<Clicker>` is clicked, the `handleClick()` function will get fired. Under
|
||||
the hood, React uses top-level event delegation to achieve high performance.
|
||||
|
||||
## Automatically Binding Callbacks
|
||||
|
||||
Just like any callback in JavaScript, if you want to refer to the component as
|
||||
`this` from the callback, you need to bind the callback to the component:
|
||||
|
||||
```javascript{3}
|
||||
var Clicker = React.createClass({
|
||||
render: function() {
|
||||
var handleClick = this.handleClick.bind(this);
|
||||
return <span onClick={handleClick}>Click me!</span>;
|
||||
},
|
||||
handleClick: function(event) {
|
||||
alert(this.ALERT_MESSAGE);
|
||||
},
|
||||
ALERT_MESSAGE: 'You clicked me!'
|
||||
});
|
||||
```
|
||||
|
||||
React provides a convenient and _efficient_ way to bind methods using
|
||||
`React.autoBind()`:
|
||||
|
||||
```javascript{3,5-7}
|
||||
var Clicker = React.createClass({
|
||||
render: function() {
|
||||
return <span onClick={this.handleClick}>Click me!</span>;
|
||||
},
|
||||
handleClick: React.autoBind(function(event) {
|
||||
alert(this.ALERT_MESSAGE);
|
||||
}),
|
||||
ALERT_MESSAGE: 'You clicked me!'
|
||||
});
|
||||
```
|
||||
|
||||
> Note:
|
||||
>
|
||||
> Binding a function allocates memory to create a new bound function. Since
|
||||
> `render()` may be invoked many times, it is a bad place to bind functions.
|
||||
> `React.autoBind()` sidesteps this issue by only binding once at instantiation
|
||||
> time.
|
||||
|
||||
## DOM Events
|
||||
|
||||
React uses [top-level event delegation](http://davidwalsh.name/event-delegate)
|
||||
to achieve high performance when implementing DOM events. For each type of DOM
|
||||
event, React adds a single top-level listener and determines which event
|
||||
handlers to execute by simulating event capturing and bubbling.
|
||||
|
||||
DOM event handlers are called with a normalized `AbstractEvent` object that has
|
||||
cross-browser compatible implementations of `stopPropagation` and
|
||||
`preventDefault()`. If you need access to the raw browser event, you can use the
|
||||
`nativeEvent` property.
|
||||
|
||||
> Note:
|
||||
>
|
||||
> The `AbstractEvent` object is JSON serializable so that React applications can
|
||||
> be executed inside web workers.
|
||||
|
||||
### Touch Events
|
||||
|
||||
If you want to use touch events, you must configure React's event system to
|
||||
initialize them:
|
||||
|
||||
```javascript
|
||||
// Invoke before calling `React.renderComponent()`.
|
||||
React.initializeTouchEvents(true);
|
||||
```
|
||||
|
||||
## Custom Events
|
||||
|
||||
Notice that event listeners are attached by simply passing them into components
|
||||
as props. For DOM components, events are handled using top-level event
|
||||
delegation. For composite components, event handling is up to the component's
|
||||
implementation.
|
||||
|
||||
Here is an example of a toggle link that fires a custom `onToggle` event:
|
||||
|
||||
```javascript
|
||||
var ToggleLink = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {isEnabled: false};
|
||||
},
|
||||
render: function() {
|
||||
return <a onClick={this.handleClick}>Toggle</a>;
|
||||
},
|
||||
handleClick: React.autoBind(function() {
|
||||
var willEnable = !this.state.isEnabled;
|
||||
if (this.props.onToggle) {
|
||||
this.props.onToggle(willEnable)
|
||||
}
|
||||
this.setState({isEnabled: willEnable});
|
||||
})
|
||||
});
|
||||
|
||||
var handleToggle = function(enabled) {
|
||||
alert(enabled ? 'Enabled.' : 'Disabled.');
|
||||
};
|
||||
var myToggleLink = <ToggleLink onToggle={handleToggle} />;
|
||||
```
|
||||
|
||||
### Common Patterns
|
||||
|
||||
With React your event handlers should be quite small. Large event handlers may
|
||||
be symptomatic of code that should be moved into helpers or into `render()`.
|
||||
Here are some common usage patterns for event handlers.
|
||||
|
||||
#### Updating State
|
||||
|
||||
The most common thing to do in response to a user action is to call
|
||||
`this.setState()` to update the component's state, which will in turn trigger
|
||||
an update to the rendered component.
|
||||
|
||||
#### Server Requests
|
||||
|
||||
Many event handlers will issue a server request to read or write some data in
|
||||
response to an event. The response handler for the request will often call
|
||||
`this.setState()`.
|
||||
|
||||
#### Invoke a Callback
|
||||
|
||||
Your component will often be a small, reusable building block that does not know
|
||||
how to respond to a user action. In these situations, we delegate the
|
||||
responsibility to the owner by exposing a handler on `this.props`. This is what
|
||||
the `ToggleLink` example above is doing.
|
||||
|
||||
#### Inter-component Communication
|
||||
|
||||
A common scenario involves communicating to **Component A** that a user action
|
||||
has occurred on **Component B**. To solve this problem, a common parent to
|
||||
both components should listen for the event on **Component B**, update its
|
||||
internal state, and pass that data into **Component A**.
|
||||
|
||||
For example, say we have two components: **Clicker**, a component that fires an
|
||||
`onCountChange` custom event, and **ClickCountLabel**, a component that displays
|
||||
the number of clicks that have happened:
|
||||
|
||||
```javascript
|
||||
var Clicker = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {count: 0};
|
||||
},
|
||||
render: function() {
|
||||
return <span onClick={this.handleClick}>Click me!</span>;
|
||||
},
|
||||
handleClick: React.autoBind(function() {
|
||||
this.setState({count: this.state.count + 1});
|
||||
if (this.props.onCountChange) {
|
||||
this.props.onCountChange(this.state.count);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
var ClickCountLabel = React.createClass({
|
||||
render: function() {
|
||||
return <p>You have clicked <strong>{this.props.count}</strong> times.</p>;
|
||||
}
|
||||
});
|
||||
|
||||
var ClickApp = React.createClass({
|
||||
render: function() {
|
||||
var count = 0;
|
||||
return (
|
||||
<div>
|
||||
<Clicker onCountChange={this.handleCountChange} />
|
||||
<ClickCountLabel count={count} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
handleCountChange: React.autoBind(function(count) {
|
||||
// Somehow update `count`.
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
In order to communicate the click count from `Clicker` to `ClickCountLabel`, we
|
||||
modify `ClickApp` to maintain state that will be passed into `ClickCountLabel`:
|
||||
|
||||
```javascript{2-4,6,15}
|
||||
var ClickApp = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {count: 0};
|
||||
},
|
||||
render: function() {
|
||||
var count = this.state.count;
|
||||
return (
|
||||
<div>
|
||||
<Clicker onCountChange={this.handleCountChange} />
|
||||
<ClickCountLabel count={count} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
handleCountChange: React.autoBind(function(count) {
|
||||
this.setState({count: count});
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
Now when `Clicker` fires the `onCountChange` event, the `ClickCountLabel` will
|
||||
get updated!
|
||||
@@ -1,9 +1,19 @@
|
||||
---
|
||||
id: docs-getting-started
|
||||
id: getting-started
|
||||
title: Getting Started
|
||||
layout: docs
|
||||
next: tutorial.html
|
||||
---
|
||||
|
||||
## JSFiddle
|
||||
|
||||
The easiest way to start hacking on React is using the following JSFiddle Hello Worlds
|
||||
|
||||
* **[React JSFiddle](http://jsfiddle.net/vjeux/kb3gN/)**
|
||||
* [React JSFiddle without JSX](http://jsfiddle.net/vjeux/VkebS/)
|
||||
|
||||
## Starter Kit
|
||||
|
||||
Download the starter kit to get started.
|
||||
|
||||
<div class="buttons-unit downloads">
|
||||
@@ -12,8 +22,6 @@ Download the starter kit to get started.
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Hello, world!
|
||||
|
||||
In the root directory of the starter kit, create a `helloworld.html` with the following contents.
|
||||
|
||||
```html
|
||||
@@ -80,6 +88,10 @@ React.renderComponent(
|
||||
);
|
||||
```
|
||||
|
||||
> Note:
|
||||
>
|
||||
> The comment parser is very strict right now, in order for it to pick up the `@jsx` modifier, two conditions are required. The `@jsx` comment block must be the first comment on the file. The comment must start with `/**` (`/*` and `//` will not work). If the parser can't find the `@jsx` comment, it will output the file without transforming it.
|
||||
|
||||
Update your HTML file as below:
|
||||
|
||||
```html{6,10}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
---
|
||||
id: docs-tutorial
|
||||
id: tutorial
|
||||
title: Tutorial
|
||||
layout: docs
|
||||
prev: getting-started.html
|
||||
next: common-questions.html
|
||||
---
|
||||
|
||||
We'll be building a simple, but realistic comments box that you can drop into a blog, similar to Disqus, LiveFyre or Facebook comments.
|
||||
|
||||
We'll provide:
|
||||
@@ -20,7 +17,11 @@ It'll also have a few neat features:
|
||||
* **Live updates:** as other users comment we'll pop them into the comment view in real time
|
||||
* **Markdown formatting:** users can use Markdown to format their text
|
||||
|
||||
## Getting started
|
||||
### Want to skip all this and just see the source?
|
||||
|
||||
[It's all on GitHub.](https://github.com/petehunt/react-tutorial)
|
||||
|
||||
### Getting started
|
||||
|
||||
For this tutorial we'll use prebuilt JavaScript files on a CDN. Open up your favorite editor and create a new HTML document:
|
||||
|
||||
@@ -46,7 +47,7 @@ For this tutorial we'll use prebuilt JavaScript files on a CDN. Open up your fav
|
||||
|
||||
For the remainder of this tutorial, we'll be writing our JavaScript code in this script tag.
|
||||
|
||||
## Your first component
|
||||
### Your first component
|
||||
|
||||
React is all about modular, composable components. For our comment box example, we'll have the following component structure:
|
||||
|
||||
@@ -76,7 +77,7 @@ React.renderComponent(
|
||||
);
|
||||
```
|
||||
|
||||
### JSX Syntax
|
||||
#### JSX Syntax
|
||||
|
||||
The first thing you'll notice is the XML-ish syntax in your JavaScript. We have a simple precompiler that translates the syntactic sugar to this plain JavaScript:
|
||||
|
||||
@@ -85,7 +86,7 @@ The first thing you'll notice is the XML-ish syntax in your JavaScript. We have
|
||||
var CommentBox = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
ReactDOM.div({
|
||||
React.DOM.div({
|
||||
className: 'commentBox',
|
||||
children: 'Hello, world! I am a CommentBox.'
|
||||
})
|
||||
@@ -100,7 +101,7 @@ React.renderComponent(
|
||||
|
||||
Its use is optional but we've found JSX syntax easier to use than plain JavaScript. Read more on the [JSX Syntax article](syntax.html).
|
||||
|
||||
### What's going on
|
||||
#### What's going on
|
||||
|
||||
We pass some methods in a JavaScript object to `React.createClass()` to create a new React component. The most important of these methods is called `render` which returns a tree of React components that will eventually render to HTML.
|
||||
|
||||
@@ -110,7 +111,7 @@ You do not have to return basic HTML. You can return a tree of components that y
|
||||
|
||||
`React.renderComponent()` instantiates the root component, starts the framework, and injects the markup into a raw DOM element, provided as the second argument.
|
||||
|
||||
# Composing components
|
||||
## Composing components
|
||||
|
||||
Let's build skeletons for `CommentList` and `CommentForm` which will, again, be simple `<div>`s:
|
||||
|
||||
@@ -154,9 +155,9 @@ var CommentBox = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
Notice how we're mixing HTML tags and components we've built. HTML components are regular React components, just like the ones you define, with one difference. The JSX compiler will automatically rewrite HTML tags to "ReactDOM.tagName" expressions and leave everything else alone. This is to prevent the pollution of the global namespace.
|
||||
Notice how we're mixing HTML tags and components we've built. HTML components are regular React components, just like the ones you define, with one difference. The JSX compiler will automatically rewrite HTML tags to "React.DOM.tagName" expressions and leave everything else alone. This is to prevent the pollution of the global namespace.
|
||||
|
||||
## Component Properties
|
||||
### Component Properties
|
||||
|
||||
Let's create our third component, `Comment`. We will want to pass it the author name and comment text so we can reuse the same code for each unique comment. First let's add some comments to the `CommentList`:
|
||||
|
||||
@@ -176,7 +177,7 @@ var CommentList = React.createClass({
|
||||
|
||||
Note that we have passed some data from the parent `CommentList` component to the child `Comment` component as both XML-like children and attributes. Data passed from parent to child is called **props**, short for properties.
|
||||
|
||||
## Using props
|
||||
### Using props
|
||||
|
||||
Let's create the Comment component. It will read the data passed to it from the CommentList and render some markup:
|
||||
|
||||
@@ -198,7 +199,7 @@ var Comment = React.createClass({
|
||||
|
||||
By surrounding a JavaScript expression in braces inside JSX (as either an attribute or child), you can drop text or React components into the tree. We access named attributes passed to the component as keys on `this.props` and any nested elements as `this.props.children`.
|
||||
|
||||
## Adding Markdown
|
||||
### Adding Markdown
|
||||
|
||||
Markdown is a simple way to format your text inline. For example, surrounding text with asterisks will make it emphasized.
|
||||
|
||||
@@ -251,15 +252,15 @@ This is a special API that intentionally makes it difficult to insert raw HTML,
|
||||
|
||||
**Remember:** by using this feature you're relying on Showdown to be secure.
|
||||
|
||||
## Hook up the data model
|
||||
### Hook up the data model
|
||||
|
||||
So far we've been inserting the comments directly in the source code. Instead, let's render a blob of JSON data into the comment list. Eventually this will come from the server, but for now, write it in your source:
|
||||
|
||||
```javascript
|
||||
// tutorial8.js
|
||||
var data = [
|
||||
{author: 'Pete Hunt', text: 'This is one comment'},
|
||||
{author: 'Jordan Walke', text: 'This is *another* comment'}
|
||||
{author: "Pete Hunt", text: "This is one comment"},
|
||||
{author: "Jordan Walke", text: "This is *another* comment"}
|
||||
];
|
||||
```
|
||||
|
||||
@@ -305,7 +306,7 @@ var CommentList = React.createClass({
|
||||
|
||||
That's it!
|
||||
|
||||
## Fetching from the server
|
||||
### Fetching from the server
|
||||
|
||||
Let's replace the hard-coded data with some dynamic data from the server. We will remove the data prop and replace it with a URL to fetch:
|
||||
|
||||
@@ -319,7 +320,7 @@ React.renderComponent(
|
||||
|
||||
This component is different from the prior components because it will have to re-render itself. The component won't have any data until the request from the server comes back, at which point the component may need to render some new comments.
|
||||
|
||||
## Reactive state
|
||||
### Reactive state
|
||||
|
||||
So far, each component has rendered itself once based on its props. `props` are immutable: they are passed from the parent and are "owned" by the parent. To implement interactions, we introduce mutable **state** to the component. `this.state` is private to the component and can be changed by calling `this.setState()`. When the state is updated, the component re-renders itself.
|
||||
|
||||
@@ -327,7 +328,7 @@ So far, each component has rendered itself once based on its props. `props` are
|
||||
|
||||
When the server fetches data, we will be changing the comment data we have. Let's add an array of comment data to the `CommentBox` component as its state:
|
||||
|
||||
```javascript{3-5}
|
||||
```javascript{3-5,10}
|
||||
// tutorial12.js
|
||||
var CommentBox = React.createClass({
|
||||
getInitialState: function() {
|
||||
@@ -347,14 +348,14 @@ var CommentBox = React.createClass({
|
||||
|
||||
`getInitialState()` executes exactly once during the lifecycle of the component and sets up the initial state of the component.
|
||||
|
||||
### Updating state
|
||||
#### Updating state
|
||||
When the component is first created, we want to GET some JSON from the server and update the state to reflect the latest data. In a real application this would be a dynamic endpoint, but for this example, we will use a static JSON file to keep things simple:
|
||||
|
||||
```javascript
|
||||
// tutorial13.json
|
||||
[
|
||||
{'author': 'Pete Hunt', 'text': 'This is one comment'},
|
||||
{'author': 'Jordan Walke', 'text': 'This is *another* comment'}
|
||||
{"author": "Pete Hunt", "text": "This is one comment"},
|
||||
{"author": "Jordan Walke", "text": "This is *another* comment"}
|
||||
]
|
||||
```
|
||||
|
||||
@@ -431,9 +432,9 @@ React.renderComponent(
|
||||
|
||||
```
|
||||
|
||||
All we have done here is move the AJAX call to a separate method and call it when the component is first loaded and every 60 seconds after that. Try running this in your browser and changing the `comments.json` file; within 5 seconds, the changes will show!
|
||||
All we have done here is move the AJAX call to a separate method and call it when the component is first loaded and every 5 seconds after that. Try running this in your browser and changing the `comments.json` file; within 5 seconds, the changes will show!
|
||||
|
||||
## Adding new comments
|
||||
### Adding new comments
|
||||
|
||||
Now it's time to build the form. Our `CommentForm` component should ask the user for their name and comment text and send a request to the server to save the comment.
|
||||
|
||||
@@ -445,27 +446,29 @@ var CommentForm = React.createClass({
|
||||
<form class="commentForm">
|
||||
<input type="text" placeholder="Your name" />
|
||||
<input type="text" placeholder="Say something..." />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Let's make the form interactive. When the user presses enter, we should clear the form, submit a request to the server, and refresh the list of comments. To start, let's listen for this event and just clear the form.
|
||||
Let's make the form interactive. When the user submits the form, we should clear it, submit a request to the server, and refresh the list of comments. To start, let's listen for the form's submit event and clear it.
|
||||
|
||||
```javascript{3-12,15,20}
|
||||
```javascript{3-13,16,21}
|
||||
// tutorial16.js
|
||||
var CommentForm = React.createClass({
|
||||
handleSubmit: React.autoBind(function() {
|
||||
handleSubmit: function() {
|
||||
var author = this.refs.author.getDOMNode().value.trim();
|
||||
var text = this.refs.text.getDOMNode().value.trim();
|
||||
if (!text || !author) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// TODO: send request to the server
|
||||
this.refs.author.getDOMNode().value = '';
|
||||
this.refs.text.getDOMNode().value = '';
|
||||
}),
|
||||
return false;
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<form class="commentForm" onSubmit={this.handleSubmit}>
|
||||
@@ -475,29 +478,30 @@ var CommentForm = React.createClass({
|
||||
placeholder="Say something..."
|
||||
ref="text"
|
||||
/>
|
||||
<input type="submit" />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### Events
|
||||
##### Events
|
||||
|
||||
React attaches event handlers to components using a camelCase naming convention. We attach an onKeyUp handler to the text field, check if the user has entered text and pressed the enter key and then clear the form fields.
|
||||
React attaches event handlers to components using a camelCase naming convention. We attach an `onSubmit` handler to the form that clears the form fields when the form is submitted with valid input.
|
||||
|
||||
`React.autoBind()` is a simple way to ensure that a method is always bound to its component. Inside the method, `this` will be bound to the component instance.
|
||||
We always return `false` from the event handler to prevent the browser's default action of submitting the form. (If you prefer, you can instead take the event as an argument and call `preventDefault()` on it – read more about [event handling](event-handling.html).)
|
||||
|
||||
#### Refs
|
||||
##### Refs
|
||||
|
||||
We use the `ref` attribute to assign a name to a child component and `this.refs` to reference the component. We can call `getDOMNode()` on a component to get the native browser DOM element.
|
||||
|
||||
#### Callbacks as props
|
||||
##### Callbacks as props
|
||||
|
||||
When a user submits a comment, we will need to refresh the list of comments to include the new one. It makes sense to do all of this logic in `CommentBox` since `CommentBox` owns the state that represents the list of comments.
|
||||
|
||||
We need to pass data from the child component to its parent. We do this by passing a `callback` in props from parent to child:
|
||||
|
||||
```javascript{13-15,30}
|
||||
```javascript{13-15,32}
|
||||
// tutorial17.js
|
||||
var CommentBox = React.createClass({
|
||||
loadCommentsFromServer: function() {
|
||||
@@ -510,9 +514,9 @@ var CommentBox = React.createClass({
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
handleCommentSubmit: React.autoBind(function(comment) {
|
||||
handleCommentSubmit: function(comment) {
|
||||
// TODO: submit to the server and refresh the list
|
||||
}),
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {data: []};
|
||||
},
|
||||
@@ -537,18 +541,19 @@ var CommentBox = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
Let's call the callback from the `CommentForm` when the user presses enter:
|
||||
Let's call the callback from the `CommentForm` when the user submits the form:
|
||||
|
||||
```javascript{6}
|
||||
// tutorial18.js
|
||||
var CommentForm = React.createClass({
|
||||
handleSubmit: React.autoBind(function() {
|
||||
handleSubmit: function() {
|
||||
var author = this.refs.author.getDOMNode().value.trim();
|
||||
var text = this.refs.text.getDOMNode().value.trim();
|
||||
this.props.onCommentSubmit({author: author, text: text});
|
||||
this.refs.author.getDOMNode().value = '';
|
||||
this.refs.text.getDOMNode().value = '';
|
||||
}),
|
||||
return false;
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<form class="commentForm" onSubmit={this.handleSubmit}>
|
||||
@@ -558,6 +563,7 @@ var CommentForm = React.createClass({
|
||||
placeholder="Say something..."
|
||||
ref="text"
|
||||
/>
|
||||
<input type="submit" />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -579,7 +585,7 @@ var CommentBox = React.createClass({
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
handleCommentSubmit: React.autoBind(function(comment) {
|
||||
handleCommentSubmit: function(comment) {
|
||||
$.ajax({
|
||||
url: this.props.url,
|
||||
data: comment,
|
||||
@@ -589,7 +595,7 @@ var CommentBox = React.createClass({
|
||||
this.setState({data: data});
|
||||
}.bind(this)
|
||||
});
|
||||
}),
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {data: []};
|
||||
},
|
||||
@@ -614,7 +620,7 @@ var CommentBox = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
## Optimization: optimistic updates
|
||||
### Optimization: optimistic updates
|
||||
|
||||
Our application is now feature complete but it feels slow to have to wait for the request to complete before your comment appears in the list. We can optimistically add this comment to the list to make the app feel faster.
|
||||
|
||||
@@ -631,7 +637,7 @@ var CommentBox = React.createClass({
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
handleCommentSubmit: React.autoBind(function(comment) {
|
||||
handleCommentSubmit: function(comment) {
|
||||
var comments = this.state.data;
|
||||
comments.push(comment);
|
||||
this.setState({data: comments});
|
||||
@@ -644,7 +650,7 @@ var CommentBox = React.createClass({
|
||||
this.setState({data: data});
|
||||
}.bind(this)
|
||||
});
|
||||
}),
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {data: []};
|
||||
},
|
||||
@@ -669,6 +675,6 @@ var CommentBox = React.createClass({
|
||||
});
|
||||
```
|
||||
|
||||
## Congrats!
|
||||
### Congrats!
|
||||
|
||||
You have just built a comment box in a few simple steps. Learn more about React in the [reference](syntax.html) or start hacking! Good luck!
|
||||
|
||||
@@ -17,16 +17,28 @@ Download the starter kit to get everything you need to
|
||||
#### <a href="http://fb.me/react-{{site.react_version}}.min.js">React Core {{site.react_version}} (production)</a>
|
||||
The compressed, production version of React core
|
||||
|
||||
```html
|
||||
<script src="http://fb.me/react-{{site.react_version}}.min.js"></script>
|
||||
```
|
||||
|
||||
#### <a href="http://fb.me/react-{{site.react_version}}.js">React Core {{site.react_version}} (development)</a>
|
||||
The uncompressed, development version of React core with inline documentation.
|
||||
|
||||
```html
|
||||
<script src="http://fb.me/react-{{site.react_version}}.js"></script>
|
||||
```
|
||||
|
||||
#### <a href="http://fb.me/JSXTransformer-{{site.react_version}}.js">JSX Transform</a>
|
||||
The JSX transformer used to support [XML syntax](/react/docs/syntax.html) in JavaScript.
|
||||
|
||||
```html
|
||||
<script src="http://fb.me/JSXTransformer-{{site.react_version}}.js"></script>
|
||||
```
|
||||
|
||||
## Bower
|
||||
|
||||
```sh
|
||||
$ bower install react
|
||||
$ bower install --save react
|
||||
```
|
||||
|
||||
## NPM
|
||||
@@ -35,10 +47,3 @@ $ bower install react
|
||||
$ npm install -g react-tools
|
||||
```
|
||||
|
||||
## Release Notes
|
||||
|
||||
**0.3** Initial public release.
|
||||
|
||||
**0.2** Standardize API & refactor component lifecycle. Normalize DOM interactions.
|
||||
|
||||
**0.1** Initial release.
|
||||
|
||||
21
docs/feed.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
layout: none
|
||||
---
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ site.name }}</title>
|
||||
<description>{{ site.description }}</description>
|
||||
<link>{{ site.url }}{{ site.baseurl }}</link>
|
||||
<atom:link href="{{ site.url }}{{ site.baseurl }}/feed.xml" rel="self" type="application/rss+xml" />
|
||||
{% for post in site.posts limit:10 %}
|
||||
<item>
|
||||
<title>{{ post.title | xml_escape }}</title>
|
||||
<description>{{ post.content | xml_escape }}</description>
|
||||
<pubDate>{{ post.date | date_to_xmlschema }}</pubDate>
|
||||
<link>{{ site.url }}{{ site.baseurl }}{{ post.url }}</link>
|
||||
<guid isPermaLink="true">{{ site.url }}{{ site.baseurl }}{{ post.url }}</guid>
|
||||
</item>
|
||||
{% endfor %}
|
||||
</channel>
|
||||
</rss>
|
||||
BIN
docs/img/blog/chatapp.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/img/blog/khan-academy-editor.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/blog/monkeys.gif
Normal file
|
After Width: | Height: | Size: 532 KiB |
BIN
docs/img/blog/propeller-logo.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
docs/img/blog/snake.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
docs/img/blog/todomvc.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/img/blog/xreact.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
@@ -3,6 +3,7 @@ layout: page
|
||||
title: A JavaScript library for building user interfaces
|
||||
id: home
|
||||
---
|
||||
|
||||
<section class="light home-section">
|
||||
<div class="marketing-row">
|
||||
<div class="marketing-col">
|
||||
@@ -15,8 +16,8 @@ id: home
|
||||
<div class="marketing-col">
|
||||
<h3>Efficient</h3>
|
||||
<p>
|
||||
React minimizes interactions with the DOM by using a mock representation
|
||||
of the DOM.
|
||||
React computes the minimal set of changes necessary to keep your DOM
|
||||
up-to-date.
|
||||
</p>
|
||||
</div>
|
||||
<div class="marketing-col">
|
||||
@@ -34,29 +35,32 @@ id: home
|
||||
<h3>A Simple Component</h3>
|
||||
<p>
|
||||
React components implement a `render()` method that takes input data and
|
||||
returns what to display. This example constructs the component using an
|
||||
XML-like syntax called JSX. Input data is passed to the component as XML
|
||||
attributes, and `render()` accesses this data via `this.props`.
|
||||
returns what to display. This example uses an XML-like syntax called
|
||||
JSX. Input data that is passed into the component can be accessed by
|
||||
`render()` via `this.props`.<br />
|
||||
<strong>JSX is optional and not required to use React.</strong>
|
||||
</p>
|
||||
<div id="helloExample"></div>
|
||||
</div>
|
||||
<div class="example">
|
||||
<h3>A Stateful Component</h3>
|
||||
<p>
|
||||
In addition to taking data from its creator (accessed via `this.props`),
|
||||
a component can maintain internal state data (accessed via
|
||||
`this.state`). When a component's state data changes, the rendered
|
||||
markup will be updated by re-invoking `render()`.
|
||||
In addition to taking input data (accessed via `this.props`), a
|
||||
component can maintain internal state data (accessed via `this.state`).
|
||||
When a component's state data changes, the rendered markup will be
|
||||
updated by re-invoking `render()`.<br />
|
||||
<strong>This example demonstrates use of React without JSX.</strong>
|
||||
</p>
|
||||
<div id="timerExample"></div>
|
||||
</div>
|
||||
<div class="example">
|
||||
<h3>An Application</h3>
|
||||
<p>
|
||||
Using properties and state, we can put together a small Todo
|
||||
application. React provides an interface to the DOM via `refs`. Although
|
||||
event handlers appear to be rendered inline, they will be
|
||||
collected and implemented using event delegation.
|
||||
Using `props` and `state`, we can put together a small Todo application.
|
||||
This example uses `state` to track the current list of items as well as
|
||||
the text that the user has entered. Although event handlers appear to be
|
||||
rendered inline, they will be collected and implemented using event
|
||||
delegation.
|
||||
</p>
|
||||
<div id="todoExample"></div>
|
||||
</div>
|
||||
|
||||
14
docs/jsx-compiler.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
layout: default
|
||||
title: JSX Compiler Service
|
||||
id: jsx-compiler
|
||||
---
|
||||
<div class="jsxCompiler">
|
||||
<h1>JSX Compiler</h1>
|
||||
<p>
|
||||
This tool demonstrates how <a href="/react/docs/syntax.html">JSX syntax</a>
|
||||
is desguared into native JavaScript.
|
||||
</p>
|
||||
<div id="jsxCompiler"></div>
|
||||
<script type="text/javascript" src="js/jsx-compiler.js"></script>
|
||||
</div>
|
||||
@@ -13,3 +13,9 @@ id: support
|
||||
## IRC
|
||||
|
||||
Many developers and users idle on Freenode.net's IRC network in **[#reactjs on freenode](irc://chat.freenode.net/reactjs)**.
|
||||
|
||||
## Twitter
|
||||
|
||||
[**#reactjs** hash tag on Twitter](https://twitter.com/search?q=%23reactjs) is used to keep up with the latest React news.
|
||||
|
||||
<center><a class="twitter-timeline" data-dnt="true" data-chrome="nofooter noheader transparent" href="https://twitter.com/search?q=%23reactjs" data-widget-id="342522405270470656"></a></center>
|
||||
|
||||
@@ -14,9 +14,9 @@ var BallmerPeakCalculator = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {bac: 0};
|
||||
},
|
||||
handleChange: React.autoBind(function() {
|
||||
handleChange: function() {
|
||||
this.setState({bac: this.refs.bac.getDOMNode().value});
|
||||
}),
|
||||
},
|
||||
render: function() {
|
||||
var bac;
|
||||
var pct;
|
||||
@@ -33,7 +33,7 @@ var BallmerPeakCalculator = React.createClass({
|
||||
<h4>Compute your Ballmer Peak:</h4>
|
||||
<p>
|
||||
If your BAC is{' '}
|
||||
<input ref="bac" type="text" onKeyUp={this.handleChange} value={this.state.bac} />
|
||||
<input ref="bac" type="text" onInput={this.handleChange} value={this.state.bac} />
|
||||
{', '}then <b>{pct}</b> of your lines of code will have bugs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -4,34 +4,66 @@
|
||||
<meta http-equiv='Content-type' content='text/html; charset=utf-8'>
|
||||
<title>Basic Example with external JSX</title>
|
||||
<link rel="stylesheet" href="../shared/css/base.css" />
|
||||
<style type="text/css" media="screen">
|
||||
.codeBox {
|
||||
padding: 7px;
|
||||
overflow:scroll;
|
||||
background-color: #eee;
|
||||
font-weight:normal;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
window.setTimeout(function() {
|
||||
var chromeClientCLI = window.chromeClientCLI;
|
||||
var errorBox = window.errorBox;
|
||||
var chromeErrorFooter = window.chromeErrorFooter;
|
||||
var chromeInstructions = window.chromeInstructions;
|
||||
var isChrome = !!window.chrome;
|
||||
chromeClientCLI.innerText =
|
||||
'open -a "Google Chrome" --new \\\n' +
|
||||
' ' + window.location.href + ' \\\n' +
|
||||
' --args --allow-file-access-from-files --user-data-dir=/tmp';
|
||||
|
||||
errorBox.innerText =
|
||||
isChrome ? 'To run in Chrome, do one of the following:' :
|
||||
'Errors loading page: Check the console.'
|
||||
chromeErrorFooter.innerText =
|
||||
isChrome ? 'If page still does not load, check console.' : '';
|
||||
|
||||
if (!isChrome) {
|
||||
chromeInstructions.innerText = "";
|
||||
}
|
||||
}, 0);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Basic Example with external JSX</h1>
|
||||
<div id="container">
|
||||
<p>
|
||||
If you can see this, React is not working right. This is probably because you're viewing
|
||||
this on your file system instead of a web server. Try running
|
||||
<pre>
|
||||
python -m SimpleHTTPServer
|
||||
</pre>
|
||||
and going to <a href="http://localhost:8080/">http://localhost:8080/</a>.
|
||||
<h4 id="errorBox" style="color: #733"></h4>
|
||||
<ol id="chromeInstructions">
|
||||
<li>
|
||||
Open this page on a Mac via the terminal command:
|
||||
<pre id="chromeClientCLI" class="codeBox">
|
||||
</li>
|
||||
<h4><i>OR</i></h4>
|
||||
<li>
|
||||
Serve this page from a web server
|
||||
<pre id="chromeServerCLI" class="codeBox">
|
||||
cd /Path/To/This/File
|
||||
python -m SimpleHTTPServer
|
||||
open -a "Google Chrome" <a href="http://localhost:8080/">http://localhost:8080/</a>. </pre>
|
||||
</li>
|
||||
</ol>
|
||||
<h4 id="chromeErrorFooter" style="color: #733"></h4>
|
||||
</p>
|
||||
</div>
|
||||
<h4>Example Details</h4>
|
||||
<ul>
|
||||
<li>
|
||||
This is built with
|
||||
<p>
|
||||
Example Details: This is built with
|
||||
<a href="https://github.com/substack/node-browserify">browserify</a>.
|
||||
</li>
|
||||
<li>
|
||||
This is written with JSX in a separate file and transformed in the browser.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
</p>
|
||||
<p>
|
||||
Learn more at
|
||||
<a href="http://facebook.github.io/react" target="_blank">facebook.github.io/react</a>.
|
||||
A separate JSX file is transformed in the browser.
|
||||
</p>
|
||||
<script src="../../build/react.js"></script>
|
||||
<script src="../../build/JSXTransformer.js"></script>
|
||||
|
||||
18
examples/jquery-bootstrap/js/app.js
vendored
@@ -67,18 +67,18 @@ var BootstrapModal = React.createClass({
|
||||
</div>
|
||||
);
|
||||
},
|
||||
onCancel: React.autoBind(function() {
|
||||
onCancel: function() {
|
||||
if (this.props.onCancel) {
|
||||
this.props.onCancel();
|
||||
}
|
||||
this.close();
|
||||
}),
|
||||
onConfirm: React.autoBind(function() {
|
||||
},
|
||||
onConfirm: function() {
|
||||
if (this.props.onConfirm) {
|
||||
this.props.onConfirm();
|
||||
}
|
||||
this.close();
|
||||
}),
|
||||
},
|
||||
close: function() {
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
@@ -90,14 +90,14 @@ var Example = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {modalVisible: false};
|
||||
},
|
||||
toggleModal: React.autoBind(function() {
|
||||
toggleModal: function() {
|
||||
this.setState({modalVisible: !this.state.modalVisible});
|
||||
}),
|
||||
handleCancel: React.autoBind(function() {
|
||||
},
|
||||
handleCancel: function() {
|
||||
if (confirm('Are you sure you want to cancel?')) {
|
||||
this.toggleModal();
|
||||
}
|
||||
}),
|
||||
},
|
||||
render: function() {
|
||||
var modal = null;
|
||||
if (this.state.modalVisible) {
|
||||
@@ -121,4 +121,4 @@ var Example = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(<Example />, document.getElementById('jqueryexample'));
|
||||
React.renderComponent(<Example />, document.getElementById('jqueryexample'));
|
||||
|
||||
@@ -383,6 +383,10 @@ body {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.submitButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Hack to remove background from Mobile Safari.
|
||||
Can't use it globally since it destroys checkboxes in Firefox and Opera
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script type="text/javascript" src="../shared/thirdparty/jquery.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="thirdparty/lodash.min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="thirdparty/backbone-min.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="thirdparty/backbone.localStorage.js" charset="utf-8"></script>
|
||||
<script type="text/javascript" src="thirdparty/backbone.localStorage-min.js" charset="utf-8"></script>
|
||||
<script type="text/jsx" src="js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -37,7 +37,7 @@ var TodoList = Backbone.Collection.extend({
|
||||
model: Todo,
|
||||
|
||||
// Save all of the todo items under the `"todos"` namespace.
|
||||
localStorage: new Store('todos-backbone'),
|
||||
localStorage: new Store('todos-react-backbone'),
|
||||
|
||||
// Filter down the list of all todo items that are finished.
|
||||
completed: function() {
|
||||
@@ -46,99 +46,61 @@ var TodoList = Backbone.Collection.extend({
|
||||
});
|
||||
},
|
||||
|
||||
// Filter down the list to only todo items that are still not finished.
|
||||
remaining: function() {
|
||||
return this.without.apply( this, this.completed() );
|
||||
return this.without.apply(this, this.completed());
|
||||
},
|
||||
|
||||
// We keep the Todos in sequential order, despite being saved by unordered
|
||||
// GUID in the database. This generates the next order number for new items.
|
||||
nextOrder: function() {
|
||||
if ( !this.length ) {
|
||||
nextOrder: function () {
|
||||
if (!this.length) {
|
||||
return 1;
|
||||
}
|
||||
return this.last().get('order') + 1;
|
||||
},
|
||||
|
||||
// Todos are sorted by their original insertion order.
|
||||
comparator: function( todo ) {
|
||||
comparator: function (todo) {
|
||||
return todo.get('order');
|
||||
}
|
||||
});
|
||||
|
||||
// Create our global collection of **Todos**.
|
||||
var Todos = new TodoList();
|
||||
|
||||
var TodoFilter;
|
||||
|
||||
var Workspace = Backbone.Router.extend({
|
||||
routes:{
|
||||
'*filter': 'setFilter'
|
||||
},
|
||||
|
||||
setFilter: function( param ) {
|
||||
// Set the current filter to be used
|
||||
TodoFilter = param.trim() || '';
|
||||
|
||||
// Trigger a collection filter event, causing hiding/unhiding
|
||||
// of Todo view items
|
||||
Todos.trigger('filter');
|
||||
}
|
||||
});
|
||||
|
||||
var TodoRouter = new Workspace();
|
||||
Backbone.history.start();
|
||||
|
||||
var Utils = {
|
||||
// https://gist.github.com/1308368
|
||||
uuid: function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b},
|
||||
pluralize: function( count, word ) {
|
||||
return count === 1 ? word : word + 's';
|
||||
},
|
||||
store: function( namespace, data ) {
|
||||
if ( arguments.length > 1 ) {
|
||||
return localStorage.setItem( namespace, JSON.stringify( data ) );
|
||||
} else {
|
||||
var store = localStorage.getItem( namespace );
|
||||
return ( store && JSON.parse( store ) ) || [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Begin React stuff
|
||||
|
||||
var ENTER_KEY = 13;
|
||||
|
||||
var TodoItem = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {editValue: this.props.todo.title};
|
||||
},
|
||||
onKeyUp: React.autoBind(function(event) {
|
||||
this.setState({editValue: event.target.value});
|
||||
var val = event.target.value.trim();
|
||||
if (event.nativeEvent.keyCode !== ENTER_KEY || !val) {
|
||||
return;
|
||||
handleSubmit: function(event) {
|
||||
var val = this.refs.editField.getDOMNode().value;
|
||||
if (val) {
|
||||
this.props.onSave(val);
|
||||
}
|
||||
this.props.onSave(val);
|
||||
}),
|
||||
onEdit: React.autoBind(function() {
|
||||
return false;
|
||||
},
|
||||
onEdit: function() {
|
||||
this.props.onEdit();
|
||||
this.refs.editField.getDOMNode().focus();
|
||||
}),
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<li class={cx({completed: this.props.todo.completed, editing: this.props.editing})}>
|
||||
<li class={cx({completed: this.props.todo.get('completed'), editing: this.props.editing})}>
|
||||
<div class="view">
|
||||
<input
|
||||
class="toggle"
|
||||
type="checkbox"
|
||||
checked={this.props.todo.completed ? 'checked' : null}
|
||||
checked={this.props.todo.get('completed') ? 'checked' : null}
|
||||
onChange={this.props.onToggle}
|
||||
/>
|
||||
<label onDoubleClick={this.onEdit}>{this.props.todo.title}</label>
|
||||
<button class="destroy" onClick={this.props.onDestroy} />
|
||||
<label onDoubleClick={this.onEdit}>{this.props.todo.get('title')}</label>
|
||||
<button class="destroy" onClick={this.props.onDestroy} />
|
||||
</div>
|
||||
<input ref="editField" class="edit" value={this.state.editValue} onKeyUp={this.onKeyUp} />
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input ref="editField" class="edit" value={this.props.todo.get('title')} />
|
||||
</form>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -164,65 +126,96 @@ var TodoFooter = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
var TodoApp = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {todos: Utils.store('todos-react'), newTodoValue: '', editing: {}};
|
||||
// An example generic Mixin that you can add to any component that should react to changes in a Backbone component.
|
||||
// The use cases we've identified thus far are for Collections -- since they trigger a change event whenever
|
||||
// any of their constituent items are changed there's no need to reconcile for regular models.
|
||||
// One caveat: this relies on getBackboneModels() to always return the same model instances throughout the
|
||||
// lifecycle of the component. If you're using this mixin correctly (it should be near the top of your
|
||||
// component hierarchy) this should not be an issue.
|
||||
var BackboneMixin = {
|
||||
componentDidMount: function() {
|
||||
// Whenever there may be a change in the Backbone data, trigger a reconcile.
|
||||
this.getBackboneModels().map(function(model) {
|
||||
model.on('add change remove', this.forceUpdate, this);
|
||||
}.bind(this));
|
||||
},
|
||||
handleKeyUp: React.autoBind(function(event) {
|
||||
this.setState({newTodoValue: event.target.value});
|
||||
var val = event.target.value.trim();
|
||||
if (event.nativeEvent.keyCode !== ENTER_KEY || !val) {
|
||||
return;
|
||||
componentWillUnmount: function() {
|
||||
// Ensure that we clean up any dangling references when the component is destroyed.
|
||||
this.getBackboneModels().map(function(model) {
|
||||
model.off(null, null, this);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
var TodoApp = React.createClass({
|
||||
mixins: [BackboneMixin],
|
||||
getInitialState: function() {
|
||||
return {editing: null};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
// Additional functionality for todomvc: fetch() the collection on init
|
||||
this.props.todos.fetch();
|
||||
},
|
||||
componentDidUpdate: function() {
|
||||
// If saving were expensive we'd listen for mutation events on Backbone and do this manually.
|
||||
// however, since saving isn't expensive this is an elegant way to keep it reactively up-to-date.
|
||||
this.props.todos.map(function(todo) {
|
||||
todo.save();
|
||||
});
|
||||
},
|
||||
getBackboneModels: function() {
|
||||
return [this.props.todos];
|
||||
},
|
||||
handleSubmit: function() {
|
||||
var val = this.refs.newField.getDOMNode().value.trim();
|
||||
if (val) {
|
||||
this.props.todos.create({
|
||||
title: val,
|
||||
completed: false,
|
||||
order: this.props.todos.nextOrder()
|
||||
});
|
||||
this.refs.newField.getDOMNode().value = '';
|
||||
}
|
||||
var todos = this.state.todos;
|
||||
todos.push({id: Utils.uuid(), title: val, completed: false});
|
||||
this.setState({todos: todos, newTodoValue: ''});
|
||||
}),
|
||||
return false;
|
||||
},
|
||||
toggleAll: function(event) {
|
||||
var checked = event.nativeEvent.target.checked;
|
||||
this.state.todos.map(function(todo) {
|
||||
todo.completed = checked;
|
||||
this.props.todos.map(function(todo) {
|
||||
todo.set('completed', checked);
|
||||
});
|
||||
this.setState({todos: this.state.todos});
|
||||
},
|
||||
toggle: function(todo) {
|
||||
todo.completed = !todo.completed;
|
||||
this.setState({todos: this.state.todos});
|
||||
},
|
||||
destroy: function(todo) {
|
||||
this.setState({todos: this.state.todos.filter(function(candidate) { return candidate.id !== todo.id; })});
|
||||
this.props.todos.remove(todo);
|
||||
},
|
||||
edit: function(todo) {
|
||||
this.state.todos.map(function(todo) { this.state.editing[todo.id] = false; }.bind(this));
|
||||
this.state.editing[todo.id] = true;
|
||||
this.setState({editing: this.state.editing});
|
||||
this.setState({editing: todo.get('id')});
|
||||
},
|
||||
save: function(todo, text) {
|
||||
todo.title = text;
|
||||
this.state.editing[todo.id] = false;
|
||||
this.setState({todos: this.state.todos, editing: this.state.editing});
|
||||
todo.set('title', text);
|
||||
this.setState({editing: null});
|
||||
},
|
||||
clearCompleted: function() {
|
||||
this.setState({todos: this.state.todos.filter(function(todo) { return !todo.completed })});
|
||||
this.props.todos.completed().map(function(todo) {
|
||||
todo.destroy();
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
Utils.store(this.props.localStorageKey || 'todos-react', this.state.todos);
|
||||
var footer = null;
|
||||
var main = null;
|
||||
var todoItems = this.state.todos.map(function(todo) {
|
||||
return <TodoItem todo={todo} onToggle={this.toggle.bind(this, todo)} onDestroy={this.destroy.bind(this, todo)} onEdit={this.edit.bind(this, todo)} editing={this.state.editing[todo.id]} onSave={this.save.bind(this, todo)} />;
|
||||
var todoItems = this.props.todos.map(function(todo) {
|
||||
return <TodoItem todo={todo} onToggle={todo.toggle.bind(todo)} onDestroy={this.destroy.bind(this, todo)} onEdit={this.edit.bind(this, todo)} editing={this.state.editing === todo.get('id')} onSave={this.save.bind(this, todo)} />;
|
||||
}.bind(this));
|
||||
|
||||
var activeTodoCount = this.state.todos.filter(function(todo) { return !todo.completed }).length;
|
||||
var activeTodoCount = this.props.todos.remaining().length;
|
||||
var completedCount = todoItems.length - activeTodoCount;
|
||||
if (activeTodoCount || completedCount) {
|
||||
footer = <TodoFooter count={activeTodoCount} completedCount={completedCount} onClearCompleted={this.clearCompleted.bind(this)} />;
|
||||
if (activeTodoCount || completedCount) {
|
||||
footer = <TodoFooter count={activeTodoCount} completedCount={completedCount} onClearCompleted={this.clearCompleted} />;
|
||||
}
|
||||
|
||||
if (todoItems.length) {
|
||||
main = (
|
||||
<section class="main">
|
||||
<input class="toggle-all" type="checkbox" onChange={this.toggleAll.bind(this)} />
|
||||
<input class="toggle-all" type="checkbox" onChange={this.toggleAll} />
|
||||
<label class="toggle-all-label">Mark all as complete</label>
|
||||
<ul class="todo-list">
|
||||
{todoItems}
|
||||
@@ -236,7 +229,9 @@ var TodoApp = React.createClass({
|
||||
<section class="todoapp">
|
||||
<header class="header">
|
||||
<h1>todos</h1>
|
||||
<input class="new-todo" placeholder="What needs to be done?" autofocus="autofocus" onKeyUp={this.handleKeyUp} value={this.state.newTodoValue} />
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input ref="newField" class="new-todo" placeholder="What needs to be done?" autofocus="autofocus" />
|
||||
</form>
|
||||
</header>
|
||||
{main}
|
||||
{footer}
|
||||
@@ -250,5 +245,4 @@ var TodoApp = React.createClass({
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(<TodoApp />, document.getElementById('todoapp'));
|
||||
React.renderComponent(<TodoApp todos={new TodoList()} />, document.getElementById('todoapp'));
|
||||
|
||||
6
examples/todomvc-backbone/thirdparty/backbone.localStorage-min.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Backbone localStorage Adapter
|
||||
* Version 1.1.4
|
||||
*
|
||||
* https://github.com/jeromegn/Backbone.localStorage
|
||||
*/(function(a,b){typeof exports=="object"?module.exports=b(require("underscore"),require("backbone")):typeof define=="function"&&define.amd?define(["underscore","backbone"],function(c,d){return b(c||a._,d||a.Backbone)}):b(_,Backbone)})(this,function(a,b){function c(){return((1+Math.random())*65536|0).toString(16).substring(1)}function d(){return c()+c()+"-"+c()+"-"+c()+"-"+c()+"-"+c()+c()+c()}return b.LocalStorage=window.Store=function(a){if(!this.localStorage)throw"Backbone.localStorage: Environment does not support localStorage.";this.name=a;var b=this.localStorage().getItem(this.name);this.records=b&&b.split(",")||[]},a.extend(b.LocalStorage.prototype,{save:function(){this.localStorage().setItem(this.name,this.records.join(","))},create:function(a){return a.id||(a.id=d(),a.set(a.idAttribute,a.id)),this.localStorage().setItem(this.name+"-"+a.id,JSON.stringify(a)),this.records.push(a.id.toString()),this.save(),this.find(a)},update:function(b){return this.localStorage().setItem(this.name+"-"+b.id,JSON.stringify(b)),a.include(this.records,b.id.toString())||this.records.push(b.id.toString()),this.save(),this.find(b)},find:function(a){return this.jsonData(this.localStorage().getItem(this.name+"-"+a.id))},findAll:function(){return(a.chain||a)(this.records).map(function(a){return this.jsonData(this.localStorage().getItem(this.name+"-"+a))},this).compact().value()},destroy:function(b){return b.isNew()?!1:(this.localStorage().removeItem(this.name+"-"+b.id),this.records=a.reject(this.records,function(a){return a===b.id.toString()}),this.save(),b)},localStorage:function(){return localStorage},jsonData:function(a){return a&&JSON.parse(a)},_clear:function(){var b=this.localStorage(),c=new RegExp("^"+this.name+"-");b.removeItem(this.name),(a.chain||a)(b).keys().filter(function(a){return c.test(a)}).each(function(a){b.removeItem(a)})},_storageSize:function(){return this.localStorage().length}}),b.LocalStorage.sync=window.Store.sync=b.localSync=function(a,c,d){var e=c.localStorage||c.collection.localStorage,f,g,h=b.$.Deferred&&b.$.Deferred();try{switch(a){case"read":f=c.id!=undefined?e.find(c):e.findAll();break;case"create":f=e.create(c);break;case"update":f=e.update(c);break;case"delete":f=e.destroy(c)}}catch(i){i.code===DOMException.QUOTA_EXCEEDED_ERR&&e._storageSize()===0?g="Private browsing is unsupported":g=i.message}return f?(d&&d.success&&(b.VERSION==="0.9.10"?d.success(c,f,d):d.success(f)),h&&h.resolve(f)):(g=g?g:"Record Not Found",d&&d.error&&(b.VERSION==="0.9.10"?d.error(c,g,d):d.error(g)),h&&h.reject(g)),d&&d.complete&&d.complete(f),h&&h.promise()},b.ajaxSync=b.sync,b.getSyncMethod=function(a){return a.localStorage||a.collection&&a.collection.localStorage?b.localSync:b.ajaxSync},b.sync=function(a,c,d){return b.getSyncMethod(c).apply(this,[a,c,d])},b.LocalStorage});
|
||||
@@ -1,130 +0,0 @@
|
||||
/**
|
||||
* Backbone localStorage Adapter
|
||||
* https://github.com/jeromegn/Backbone.localStorage
|
||||
*/
|
||||
|
||||
(function(_, Backbone) {
|
||||
// A simple module to replace `Backbone.sync` with *localStorage*-based
|
||||
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
|
||||
// as that.
|
||||
|
||||
// Hold reference to Underscore.js and Backbone.js in the closure in order
|
||||
// to make things work even if they are removed from the global namespace
|
||||
|
||||
// Generate four random hex digits.
|
||||
function S4() {
|
||||
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
|
||||
};
|
||||
|
||||
// Generate a pseudo-GUID by concatenating random hexadecimal.
|
||||
function guid() {
|
||||
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
|
||||
};
|
||||
|
||||
// Our Store is represented by a single JS object in *localStorage*. Create it
|
||||
// with a meaningful name, like the name you'd give a table.
|
||||
// window.Store is deprectated, use Backbone.LocalStorage instead
|
||||
Backbone.LocalStorage = window.Store = function(name) {
|
||||
this.name = name;
|
||||
var store = this.localStorage().getItem(this.name);
|
||||
this.records = (store && store.split(",")) || [];
|
||||
};
|
||||
|
||||
_.extend(Backbone.LocalStorage.prototype, {
|
||||
|
||||
// Save the current state of the **Store** to *localStorage*.
|
||||
save: function() {
|
||||
this.localStorage().setItem(this.name, this.records.join(","));
|
||||
},
|
||||
|
||||
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
|
||||
// have an id of it's own.
|
||||
create: function(model) {
|
||||
if (!model.id) {
|
||||
model.id = guid();
|
||||
model.set(model.idAttribute, model.id);
|
||||
}
|
||||
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
|
||||
this.records.push(model.id.toString());
|
||||
this.save();
|
||||
return model.toJSON();
|
||||
},
|
||||
|
||||
// Update a model by replacing its copy in `this.data`.
|
||||
update: function(model) {
|
||||
this.localStorage().setItem(this.name+"-"+model.id, JSON.stringify(model));
|
||||
if (!_.include(this.records, model.id.toString())) this.records.push(model.id.toString()); this.save();
|
||||
return model.toJSON();
|
||||
},
|
||||
|
||||
// Retrieve a model from `this.data` by id.
|
||||
find: function(model) {
|
||||
return JSON.parse(this.localStorage().getItem(this.name+"-"+model.id));
|
||||
},
|
||||
|
||||
// Return the array of all models currently in storage.
|
||||
findAll: function() {
|
||||
return _(this.records).chain()
|
||||
.map(function(id){return JSON.parse(this.localStorage().getItem(this.name+"-"+id));}, this)
|
||||
.compact()
|
||||
.value();
|
||||
},
|
||||
|
||||
// Delete a model from `this.data`, returning it.
|
||||
destroy: function(model) {
|
||||
this.localStorage().removeItem(this.name+"-"+model.id);
|
||||
this.records = _.reject(this.records, function(record_id){return record_id == model.id.toString();});
|
||||
this.save();
|
||||
return model;
|
||||
},
|
||||
|
||||
localStorage: function() {
|
||||
return localStorage;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// localSync delegate to the model or collection's
|
||||
// *localStorage* property, which should be an instance of `Store`.
|
||||
// window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead
|
||||
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) {
|
||||
var store = model.localStorage || model.collection.localStorage;
|
||||
|
||||
var resp, syncDfd = $.Deferred && $.Deferred(); //If $ is having Deferred - use it.
|
||||
|
||||
switch (method) {
|
||||
case "read": resp = model.id != undefined ? store.find(model) : store.findAll(); break;
|
||||
case "create": resp = store.create(model); break;
|
||||
case "update": resp = store.update(model); break;
|
||||
case "delete": resp = store.destroy(model); break;
|
||||
}
|
||||
|
||||
if (resp) {
|
||||
if (options && options.success) options.success(resp);
|
||||
if (syncDfd) syncDfd.resolve();
|
||||
} else {
|
||||
if (options && options.error) options.error("Record not found");
|
||||
if (syncDfd) syncDfd.reject();
|
||||
}
|
||||
|
||||
return syncDfd && syncDfd.promise();
|
||||
};
|
||||
|
||||
Backbone.ajaxSync = Backbone.sync;
|
||||
|
||||
Backbone.getSyncMethod = function(model) {
|
||||
if(model.localStorage || (model.collection && model.collection.localStorage))
|
||||
{
|
||||
return Backbone.localSync;
|
||||
}
|
||||
|
||||
return Backbone.ajaxSync;
|
||||
};
|
||||
|
||||
// Override 'Backbone.sync' to default to localSync,
|
||||
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
|
||||
Backbone.sync = function(method, model, options) {
|
||||
return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
|
||||
};
|
||||
|
||||
})(_, Backbone);
|
||||
@@ -418,4 +418,8 @@ body {
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.submitButton {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -31,24 +31,33 @@ function cx(obj) {
|
||||
return s;
|
||||
}
|
||||
|
||||
var ENTER_KEY = 13;
|
||||
|
||||
var TodoItem = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {editValue: this.props.todo.title};
|
||||
},
|
||||
onKeyUp: function(event) {
|
||||
this.setState({editValue: event.target.value});
|
||||
var val = event.target.value.trim();
|
||||
if (event.nativeEvent.keyCode !== ENTER_KEY || !val) {
|
||||
return;
|
||||
handleSubmit: function() {
|
||||
var val = this.state.editText;
|
||||
if (val) {
|
||||
this.props.onSave(val);
|
||||
this.setState({editField: ''});
|
||||
}
|
||||
this.props.onSave(val);
|
||||
return false;
|
||||
},
|
||||
onEdit: function() {
|
||||
handleEdit: function() {
|
||||
this.props.onEdit();
|
||||
this.refs.editField.getDOMNode().focus();
|
||||
},
|
||||
handleKey: function(event) {
|
||||
if (event.nativeEvent.keyCode === 27) {
|
||||
this.handleSubmit();
|
||||
}
|
||||
this.setState({editText: event.target.value});
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {editText: this.props.todo.title};
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
if (nextProps.todo.title !== this.props.todo.title) {
|
||||
this.setState(this.getInitialState());
|
||||
}
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<li class={cx({completed: this.props.todo.completed, editing: this.props.editing})}>
|
||||
@@ -59,10 +68,19 @@ var TodoItem = React.createClass({
|
||||
checked={this.props.todo.completed ? 'checked' : null}
|
||||
onChange={this.props.onToggle}
|
||||
/>
|
||||
<label onDoubleClick={this.onEdit.bind(this)}>{this.props.todo.title}</label>
|
||||
<label onDoubleClick={this.handleEdit}>{this.props.todo.title}</label>
|
||||
<button class="destroy" onClick={this.props.onDestroy} />
|
||||
</div>
|
||||
<input ref="editField" class="edit" value={this.state.editValue} onKeyUp={this.onKeyUp.bind(this)} />
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
ref="editField"
|
||||
class="edit"
|
||||
value={this.state.editText}
|
||||
onBlur={this.handleSubmit}
|
||||
onKeyUp={this.handleKey}
|
||||
/>
|
||||
<input type="submit" class="submitButton" />
|
||||
</form>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -91,25 +109,24 @@ var TodoFooter = React.createClass({
|
||||
var TodoApp = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
todos: Utils.store('todos-react'),
|
||||
newTodoValue: '',
|
||||
todos: Utils.store('react-todos'),
|
||||
editing: {}
|
||||
};
|
||||
},
|
||||
|
||||
handleKeyUp: function(event) {
|
||||
this.setState({newTodoValue: event.target.value});
|
||||
var val = event.target.value.trim();
|
||||
if (event.nativeEvent.keyCode !== ENTER_KEY || !val) {
|
||||
return;
|
||||
handleSubmit: function() {
|
||||
var val = this.refs.newField.getDOMNode().value.trim();
|
||||
if (val) {
|
||||
var todos = this.state.todos;
|
||||
var newTodo = {
|
||||
id: Utils.uuid(),
|
||||
title: val,
|
||||
completed: false
|
||||
};
|
||||
this.setState({todos: todos.concat([newTodo])});
|
||||
this.refs.newField.getDOMNode().value = '';
|
||||
}
|
||||
var todos = this.state.todos;
|
||||
todos.push({
|
||||
id: Utils.uuid(),
|
||||
title: val,
|
||||
completed: false
|
||||
});
|
||||
this.setState({todos: todos, newTodoValue: ''});
|
||||
return false;
|
||||
},
|
||||
|
||||
toggleAll: function(event) {
|
||||
@@ -154,7 +171,7 @@ var TodoApp = React.createClass({
|
||||
},
|
||||
|
||||
render: function() {
|
||||
Utils.store(this.props.localStorageKey || 'todos-react', this.state.todos);
|
||||
Utils.store('react-todos', this.state.todos);
|
||||
var footer = null;
|
||||
var main = null;
|
||||
var todoItems = this.state.todos.map(function(todo) {
|
||||
@@ -200,13 +217,15 @@ var TodoApp = React.createClass({
|
||||
<section class="todoapp">
|
||||
<header class="header">
|
||||
<h1>todos</h1>
|
||||
<input
|
||||
class="new-todo"
|
||||
placeholder="What needs to be done?"
|
||||
autofocus="autofocus"
|
||||
onKeyUp={this.handleKeyUp.bind(this)}
|
||||
value={this.state.newTodoValue}
|
||||
/>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
ref="newField"
|
||||
class="new-todo"
|
||||
placeholder="What needs to be done?"
|
||||
autofocus="autofocus"
|
||||
/>
|
||||
<input type="submit" class="submitButton" />
|
||||
</form>
|
||||
</header>
|
||||
{main}
|
||||
{footer}
|
||||
|
||||
@@ -76,19 +76,8 @@ var transformer = {
|
||||
after: [simpleBannerify]
|
||||
};
|
||||
|
||||
var test = {
|
||||
entries: [
|
||||
"./build/modules/test/all.js",
|
||||
"./build/modules/**/__tests__/*-test.js"
|
||||
],
|
||||
outfile: './build/react-test.js',
|
||||
debug: false,
|
||||
standalone: false
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
basic: basic,
|
||||
test: test,
|
||||
min: min,
|
||||
transformer: transformer
|
||||
};
|
||||
|
||||
@@ -12,6 +12,6 @@ module.exports = {
|
||||
return gzip.zip(contents, {}).length;
|
||||
}
|
||||
},
|
||||
cache: "build/.sizecache.json"
|
||||
cache: ".grunt/sizecache.json"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,18 @@ var rootIDs = [
|
||||
|
||||
var debug = {
|
||||
rootIDs: rootIDs,
|
||||
configFile: "grunt/config/jsx/debug.json"
|
||||
configFile: "grunt/config/jsx/debug.json",
|
||||
sourceDir: "src",
|
||||
outputDir: "build/modules"
|
||||
};
|
||||
|
||||
var jasmine = {
|
||||
rootIDs: [
|
||||
"all"
|
||||
],
|
||||
configFile: debug.configFile,
|
||||
sourceDir: "vendor/jasmine",
|
||||
outputDir: "build/jasmine"
|
||||
};
|
||||
|
||||
var test = {
|
||||
@@ -14,16 +25,21 @@ var test = {
|
||||
"test/all.js",
|
||||
"**/__tests__/*.js"
|
||||
]),
|
||||
configFile: debug.configFile
|
||||
configFile: "grunt/config/jsx/test.json",
|
||||
sourceDir: "src",
|
||||
outputDir: "build/modules"
|
||||
};
|
||||
|
||||
var release = {
|
||||
rootIDs: rootIDs,
|
||||
configFile: "grunt/config/jsx/release.json"
|
||||
configFile: "grunt/config/jsx/release.json",
|
||||
sourceDir: "src",
|
||||
outputDir: "build/modules"
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
debug: debug,
|
||||
jasmine: jasmine,
|
||||
test: test,
|
||||
release: release
|
||||
};
|
||||
|
||||
7
grunt/config/jsx/test.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"debug": true,
|
||||
"mocking": true,
|
||||
"constants": {
|
||||
"__DEV__": true
|
||||
}
|
||||
}
|
||||
1
grunt/config/npm.js
Normal file
@@ -0,0 +1 @@
|
||||
exports.pack = {};
|
||||
@@ -4,5 +4,8 @@ exports.run = {
|
||||
port: 8080,
|
||||
harness: "test/phantom-harness.js",
|
||||
// Run `grunt test --debug` to enable in-browser testing.
|
||||
debug: !!grunt.option("debug")
|
||||
debug: !!grunt.option("debug"),
|
||||
tests: [
|
||||
"**/__tests__/*-test.js"
|
||||
]
|
||||
};
|
||||
|
||||
24
grunt/config/populist.js
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
var jasmine = {
|
||||
rootDirectory: "build/jasmine",
|
||||
// This syntax means to require and expose the "jasmine" module
|
||||
// (build/jasmine/jasmine.js) as global.jasmine, and to require the
|
||||
// "all" module (build/jasmine/all.js) but not expose it globally.
|
||||
args: ["jasmine:jasmine", "all:"],
|
||||
outfile: "./build/jasmine.js"
|
||||
};
|
||||
|
||||
var test = {
|
||||
rootDirectory: "build/modules",
|
||||
args: ["test/all:"],
|
||||
requires: [
|
||||
"**/__tests__/*-test.js"
|
||||
],
|
||||
outfile: './build/react-test.js'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
jasmine: jasmine,
|
||||
test: test
|
||||
};
|
||||
@@ -12,7 +12,6 @@ module.exports = function() {
|
||||
// More/better assertions
|
||||
// grunt.config.requires('outfile');
|
||||
// grunt.config.requires('entries');
|
||||
config.requires = config.requires || {};
|
||||
config.transforms = config.transforms || [];
|
||||
config.after = config.after || [];
|
||||
if (typeof config.after === 'function') {
|
||||
@@ -24,9 +23,20 @@ module.exports = function() {
|
||||
var bundle = browserify(entries);
|
||||
|
||||
// Make sure the things that need to be exposed are.
|
||||
// TODO: support a blob pattern maybe?
|
||||
for (var name in config.requires) {
|
||||
bundle.require(config.requires[name], { expose: name });
|
||||
var requires = config.requires || {};
|
||||
if (requires instanceof Array) {
|
||||
grunt.file.expand({
|
||||
nonull: true, // Keep IDs that don't expand to anything.
|
||||
cwd: "src"
|
||||
}, requires).forEach(function(name) {
|
||||
bundle.require("./build/modules/" + name, {
|
||||
expose: name.replace(/\.js$/i, "")
|
||||
});
|
||||
});
|
||||
} else if (typeof requires === "object") {
|
||||
Object.keys(requires).forEach(function(name) {
|
||||
bundle.require(requires[name], { expose: name });
|
||||
});
|
||||
}
|
||||
|
||||
// Extract other options
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
var exec = require("child_process").exec;
|
||||
var expand = require("grunt").file.expand;
|
||||
var grunt = require("grunt");
|
||||
var expand = grunt.file.expand;
|
||||
var spawn = grunt.util.spawn;
|
||||
|
||||
module.exports = function() {
|
||||
var done = this.async();
|
||||
var config = this.data;
|
||||
|
||||
var args = [
|
||||
"bin/jsx",
|
||||
"src",
|
||||
"build/modules"
|
||||
"--cache-dir", ".module-cache",
|
||||
"--relativize",
|
||||
"--follow-requires",
|
||||
config.sourceDir,
|
||||
config.outputDir
|
||||
];
|
||||
|
||||
var rootIDs = expand({
|
||||
@@ -23,5 +26,18 @@ module.exports = function() {
|
||||
args.push.apply(args, rootIDs);
|
||||
args.push("--config", config.configFile);
|
||||
|
||||
exec(args.join(" "), done);
|
||||
var child = spawn({
|
||||
cmd: "bin/jsx",
|
||||
args: args
|
||||
}, function(error, result, code) {
|
||||
if (error) {
|
||||
grunt.log.error(error);
|
||||
done(false);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
child.stdout.pipe(process.stdout);
|
||||
child.stderr.pipe(process.stderr);
|
||||
};
|
||||
|
||||
100
grunt/tasks/npm.js
Normal file
@@ -0,0 +1,100 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require("assert");
|
||||
var path = require("path");
|
||||
var grunt = require("grunt");
|
||||
var spawn = grunt.util.spawn;
|
||||
|
||||
module.exports = function() {
|
||||
var done = this.async();
|
||||
|
||||
function run(cmd, args, opts, callback) {
|
||||
assert.strictEqual(typeof cmd, "string");
|
||||
assert.ok(args instanceof Array);
|
||||
|
||||
if (typeof opts === "function" && !callback) {
|
||||
callback = opts;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
assert.strictEqual(typeof opts, "object");
|
||||
assert.strictEqual(typeof callback, "function");
|
||||
|
||||
grunt.log.writeln("> " + cmd + " " + args.join(" "));
|
||||
|
||||
// var proc =
|
||||
spawn({
|
||||
cmd: cmd,
|
||||
args: args,
|
||||
opts: opts
|
||||
}, function(error, result, code) {
|
||||
if (error) {
|
||||
grunt.log.error(error);
|
||||
done(false);
|
||||
} else {
|
||||
callback(result, code);
|
||||
}
|
||||
});
|
||||
|
||||
// Uncomment these to see the output of the commands.
|
||||
// proc.stdout.pipe(process.stdout);
|
||||
// proc.stderr.pipe(process.stderr);
|
||||
}
|
||||
|
||||
var pkg = grunt.config.data.pkg;
|
||||
var tgz = pkg.name + "-" + pkg.version + ".tgz";
|
||||
|
||||
grunt.log.writeln("Packing " + tgz + " (this could take a while)...");
|
||||
|
||||
run("npm", ["pack", "--verbose", "."], function() {
|
||||
require("tmp").dir(function(err, dir) {
|
||||
if (err) {
|
||||
grunt.log.error(err);
|
||||
done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
run("cp", [tgz, dir], function() {
|
||||
run("npm", [
|
||||
"install",
|
||||
"--production",
|
||||
tgz
|
||||
], { cwd: dir }, function() {
|
||||
var nodePath = path.join(dir, "node_modules");
|
||||
var pkgDir = path.join(nodePath, pkg.name);
|
||||
var doneCount = 2;
|
||||
|
||||
// Make sure that bin/jsx is runnable by echoing main.js.
|
||||
run("bin/jsx", ["main.js"], {
|
||||
cwd: pkgDir
|
||||
}, function(result) {
|
||||
assert.ok(result.stdout.indexOf("transform") >= 0, result.stdout);
|
||||
|
||||
if (--doneCount === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure the .transform package method works.
|
||||
run("node", [
|
||||
"--print",
|
||||
'require("react-tools").transform(' +
|
||||
JSON.stringify(
|
||||
"/** @jsx React.DOM */ <div>oyez</div>;"
|
||||
) + ')'
|
||||
], {
|
||||
env: { NODE_PATH: nodePath }
|
||||
}, function(result, code) {
|
||||
assert.ok(result.stdout.indexOf(
|
||||
'React.DOM.div(null, "oyez");'
|
||||
) >= 0, result.stdout);
|
||||
|
||||
if (--doneCount === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require("assert");
|
||||
var fs = require("fs");
|
||||
var grunt = require("grunt");
|
||||
var spawn = grunt.util.spawn;
|
||||
var semver = require("semver");
|
||||
@@ -37,6 +38,14 @@ function run(config, done) {
|
||||
args.push("--debug");
|
||||
}
|
||||
|
||||
args.push("--tests");
|
||||
grunt.file.expand({
|
||||
nonull: true,
|
||||
cwd: "src"
|
||||
}, config.tests || []).forEach(function(file) {
|
||||
args.push(file.replace(/\.js$/i, ""));
|
||||
});
|
||||
|
||||
var child = spawn({
|
||||
cmd: phantomjs,
|
||||
args: args
|
||||
@@ -50,11 +59,13 @@ module.exports = function() {
|
||||
var config = this.data;
|
||||
var done = this.async();
|
||||
|
||||
spawn({
|
||||
cmd: phantomjs,
|
||||
args: ["--version"]
|
||||
}, function(error, result, code) {
|
||||
checkVersion(error, result, code);
|
||||
run(config, done);
|
||||
fs.chmod(phantomjs, 755, function(err) {
|
||||
spawn({
|
||||
cmd: phantomjs,
|
||||
args: ["--version"]
|
||||
}, function(error, result, code) {
|
||||
checkVersion(error, result, code);
|
||||
run(config, done);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
28
grunt/tasks/populist.js
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
var grunt = require('grunt');
|
||||
|
||||
module.exports = function() {
|
||||
var config = this.data;
|
||||
var done = this.async();
|
||||
|
||||
// create the bundle we'll work with
|
||||
var args = config.args;
|
||||
|
||||
// Make sure the things that need to be exposed are.
|
||||
var requires = config.requires || [];
|
||||
grunt.file.expand({
|
||||
nonull: true, // Keep IDs that don't expand to anything.
|
||||
cwd: config.rootDirectory
|
||||
}, requires).forEach(function(name) {
|
||||
args.push(name.replace(/\.js$/i, ""));
|
||||
});
|
||||
|
||||
require("populist").buildP({
|
||||
rootDirectory: config.rootDirectory,
|
||||
args: args
|
||||
}).then(function(output) {
|
||||
grunt.file.write(config.outfile, output);
|
||||
done();
|
||||
});
|
||||
};
|
||||
2
main.js
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var React = require('./build/React');
|
||||
var React = require('./build/modules/React');
|
||||
var visitors = require('./vendor/fbtransform/visitors').transformVisitors;
|
||||
var transform = require('./vendor/fbtransform/lib/transform').transform;
|
||||
|
||||
|
||||
33
package.json
@@ -1,10 +1,13 @@
|
||||
{
|
||||
"name": "react-tools",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"keywords": [
|
||||
"clientside"
|
||||
"react",
|
||||
"jsx",
|
||||
"transformer",
|
||||
"view"
|
||||
],
|
||||
"homepage": "https://facebook.github.io/react",
|
||||
"homepage": "http://facebook.github.io/react",
|
||||
"bugs": "https://github.com/facebook/react/issues",
|
||||
"licenses": [
|
||||
{
|
||||
@@ -16,9 +19,9 @@
|
||||
"README.md",
|
||||
"main.js",
|
||||
"bin/jsx",
|
||||
"build/React.js",
|
||||
"build/modules/",
|
||||
"vendor/fbtransform/",
|
||||
"vendor/woodchipper.js"
|
||||
"vendor/constants.js"
|
||||
],
|
||||
"main": "main.js",
|
||||
"bin": {
|
||||
@@ -33,25 +36,31 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"base62": "~0.1.1",
|
||||
"commoner": "~0.6.8",
|
||||
"esprima": "git://github.com/facebook/esprima#fb-harmony",
|
||||
"recast": "~0.3.3"
|
||||
"commoner": "~0.8.4",
|
||||
"esprima": "https://github.com/facebook/esprima/tarball/a3e0ea3979eb8d54d8bfade220c272903f928b1e",
|
||||
"recast": "~0.4.8",
|
||||
"source-map": "~0.1.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "~2.14.2",
|
||||
"browserify": "~2.24.1",
|
||||
"wrapup": "~0.12.0",
|
||||
"populist": "~0.1.3",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"grunt-contrib-jshint": "~0.5.4",
|
||||
"grunt-contrib-jshint": "~0.6.0",
|
||||
"optimist": "~0.4.0",
|
||||
"phantomjs": ">= 1.9.0",
|
||||
"semver": ">= 1.1.4",
|
||||
"source-map": "~0.1.22",
|
||||
"semver": "~2.0.0",
|
||||
"uglify-js": "~2.3.6",
|
||||
"grunt-contrib-clean": "~0.4.1",
|
||||
"grunt-compare-size": "~0.4.0",
|
||||
"gzip-js": "~0.3.2",
|
||||
"tmp": "~0.0.18",
|
||||
"grunt-contrib-compress": "~0.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"preferGlobal": true
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"globals": {
|
||||
"__DEV__": false,
|
||||
"require": false,
|
||||
"module": false
|
||||
"module": false,
|
||||
"exports": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ var ReactCompositeComponent = require('ReactCompositeComponent');
|
||||
var ReactComponent = require('ReactComponent');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactPropTypes = require('ReactPropTypes');
|
||||
var ReactServerRendering = require('ReactServerRendering');
|
||||
|
||||
var ReactDefaultInjection = require('ReactDefaultInjection');
|
||||
|
||||
@@ -29,15 +31,16 @@ ReactDefaultInjection.inject();
|
||||
|
||||
var React = {
|
||||
DOM: ReactDOM,
|
||||
PropTypes: ReactPropTypes,
|
||||
initializeTouchEvents: function(shouldUseTouch) {
|
||||
ReactMount.useTouchEvents = shouldUseTouch;
|
||||
},
|
||||
autoBind: ReactCompositeComponent.autoBind,
|
||||
createClass: ReactCompositeComponent.createClass,
|
||||
createComponentRenderer: ReactMount.createComponentRenderer,
|
||||
constructAndRenderComponent: ReactMount.constructAndRenderComponent,
|
||||
constructAndRenderComponentByID: ReactMount.constructAndRenderComponentByID,
|
||||
renderComponent: ReactMount.renderComponent,
|
||||
renderComponentToString: ReactServerRendering.renderComponentToString,
|
||||
unmountAndReleaseReactRootNode: ReactMount.unmountAndReleaseReactRootNode,
|
||||
isValidComponent: ReactComponent.isValidComponent
|
||||
};
|
||||
|
||||
@@ -16,14 +16,17 @@
|
||||
* @providesModule ReactComponent
|
||||
*/
|
||||
|
||||
/*jslint evil: true */
|
||||
|
||||
"use strict";
|
||||
|
||||
var ExecutionEnvironment = require('ExecutionEnvironment');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var ReactID = require('ReactID');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactOwner = require('ReactOwner');
|
||||
var ReactReconcileTransaction = require('ReactReconcileTransaction');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
@@ -35,6 +38,12 @@ var merge = require('merge');
|
||||
*/
|
||||
var OWNER = '{owner}';
|
||||
|
||||
/**
|
||||
* Props key that determines if a component's key was already validated.
|
||||
* @private
|
||||
*/
|
||||
var IS_KEY_VALIDATED = '{is.key.validated}';
|
||||
|
||||
/**
|
||||
* Every React component is in one of these life cycles.
|
||||
*/
|
||||
@@ -50,6 +59,78 @@ var ComponentLifeCycle = keyMirror({
|
||||
UNMOUNTED: null
|
||||
});
|
||||
|
||||
/**
|
||||
* Warn if there's no key explicitly set on dynamic arrays of children.
|
||||
* This allows us to keep track of children between updates.
|
||||
*/
|
||||
|
||||
var ownerHasWarned = {};
|
||||
|
||||
/**
|
||||
* Warn if the component doesn't have an explicit key assigned to it.
|
||||
* This component is in an array. The array could grow and shrink or be
|
||||
* reordered. All children, that hasn't already been validated, are required to
|
||||
* have a "key" property assigned to it.
|
||||
*
|
||||
* @internal
|
||||
* @param {ReactComponent} component Component that requires a key.
|
||||
*/
|
||||
function validateExplicitKey(component) {
|
||||
if (component[IS_KEY_VALIDATED] || component.props.key != null) {
|
||||
return;
|
||||
}
|
||||
component[IS_KEY_VALIDATED] = true;
|
||||
|
||||
// We can't provide friendly warnings for top level components.
|
||||
if (!ReactCurrentOwner.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Name of the component whose render method tried to pass children.
|
||||
var currentName = ReactCurrentOwner.current.constructor.displayName;
|
||||
if (ownerHasWarned.hasOwnProperty(currentName)) {
|
||||
return;
|
||||
}
|
||||
ownerHasWarned[currentName] = true;
|
||||
|
||||
var message = 'Each child in an array should have a unique "key" prop. ' +
|
||||
'Check the render method of ' + currentName + '.';
|
||||
if (!component.isOwnedBy(ReactCurrentOwner.current)) {
|
||||
// Name of the component that originally created this child.
|
||||
var childOwnerName =
|
||||
component.props[OWNER] && component.props[OWNER].constructor.displayName;
|
||||
|
||||
// Usually the current owner is the offender, but if it accepts
|
||||
// children as a property, it may be the creator of the child that's
|
||||
// responsible for assigning it a key.
|
||||
message += ' It was passed a child from ' + childOwnerName + '.';
|
||||
}
|
||||
|
||||
console.warn(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that every component either is passed in a static location or, if
|
||||
* if it's passed in an array, has an explicit key property defined.
|
||||
*
|
||||
* @internal
|
||||
* @param {*} component Statically passed child of any type.
|
||||
* @return {boolean}
|
||||
*/
|
||||
function validateChildKeys(component) {
|
||||
if (Array.isArray(component)) {
|
||||
for (var i = 0; i < component.length; i++) {
|
||||
var child = component[i];
|
||||
if (ReactComponent.isValidComponent(child)) {
|
||||
validateExplicitKey(child);
|
||||
}
|
||||
}
|
||||
} else if (ReactComponent.isValidComponent(component)) {
|
||||
// This component was passed in a valid location.
|
||||
component[IS_KEY_VALIDATED] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Components are the basic units of composition in React.
|
||||
*
|
||||
@@ -90,6 +171,23 @@ var ReactComponent = {
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a key string that identifies a component within a set.
|
||||
*
|
||||
* @param {*} component A component that could contain a manual key.
|
||||
* @param {number} index Index that is used if a manual key is not provided.
|
||||
* @return {string}
|
||||
* @internal
|
||||
*/
|
||||
getKey: function(component, index) {
|
||||
if (component && component.props && component.props.key != null) {
|
||||
// Explicit key
|
||||
return '' + component.props.key;
|
||||
}
|
||||
// Implicit key determined by the index in the set
|
||||
return '' + index;
|
||||
},
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@@ -134,53 +232,57 @@ var ReactComponent = {
|
||||
*/
|
||||
Mixin: {
|
||||
|
||||
/**
|
||||
* Checks whether or not this component is mounted.
|
||||
*
|
||||
* @return {boolean} True if mounted, false otherwise.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
isMounted: function() {
|
||||
return this._lifeCycleState === ComponentLifeCycle.MOUNTED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the DOM node rendered by this component.
|
||||
*
|
||||
* @return {?DOMElement} The root node of this component.
|
||||
* @return {DOMElement} The root node of this component.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
getDOMNode: function() {
|
||||
invariant(
|
||||
ExecutionEnvironment.canUseDOM,
|
||||
'getDOMNode(): The DOM is not supported in the current environment.'
|
||||
);
|
||||
invariant(
|
||||
this._lifeCycleState === ComponentLifeCycle.MOUNTED,
|
||||
this.isMounted(),
|
||||
'getDOMNode(): A component must be mounted to have a DOM node.'
|
||||
);
|
||||
var rootNode = this._rootNode;
|
||||
if (!rootNode) {
|
||||
rootNode = document.getElementById(this._rootNodeID);
|
||||
if (!rootNode) {
|
||||
// TODO: Log the frequency that we reach this path.
|
||||
rootNode = ReactMount.findReactRenderedDOMNodeSlow(this._rootNodeID);
|
||||
}
|
||||
this._rootNode = rootNode;
|
||||
}
|
||||
return rootNode;
|
||||
return ReactID.getNode(this._rootNodeID);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a subset of the props.
|
||||
*
|
||||
* @param {object} partialProps Subset of the next props.
|
||||
* @param {?function} callback Called after props are updated.
|
||||
* @final
|
||||
* @public
|
||||
*/
|
||||
setProps: function(partialProps) {
|
||||
this.replaceProps(merge(this.props, partialProps));
|
||||
setProps: function(partialProps, callback) {
|
||||
// Merge with `_pendingProps` if it exists, otherwise with existing props.
|
||||
this.replaceProps(
|
||||
merge(this._pendingProps || this.props, partialProps),
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces all of the props.
|
||||
*
|
||||
* @param {object} props New props.
|
||||
* @param {?function} callback Called after props are updated.
|
||||
* @final
|
||||
* @public
|
||||
*/
|
||||
replaceProps: function(props) {
|
||||
replaceProps: function(props, callback) {
|
||||
invariant(
|
||||
!this.props[OWNER],
|
||||
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
|
||||
@@ -189,9 +291,8 @@ var ReactComponent = {
|
||||
'`render` method to pass the correct value as props to the component ' +
|
||||
'where it is created.'
|
||||
);
|
||||
var transaction = ReactComponent.ReactReconcileTransaction.getPooled();
|
||||
transaction.perform(this.receiveProps, this, props, transaction);
|
||||
ReactComponent.ReactReconcileTransaction.release(transaction);
|
||||
this._pendingProps = props;
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -206,13 +307,31 @@ var ReactComponent = {
|
||||
*/
|
||||
construct: function(initialProps, children) {
|
||||
this.props = initialProps || {};
|
||||
if (typeof children !== 'undefined') {
|
||||
this.props.children = children;
|
||||
}
|
||||
// Record the component responsible for creating this component.
|
||||
this.props[OWNER] = ReactCurrentOwner.current;
|
||||
// All components start unmounted.
|
||||
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
|
||||
|
||||
this._pendingProps = null;
|
||||
this._pendingCallbacks = null;
|
||||
|
||||
// Children can be more than one argument
|
||||
var childrenLength = arguments.length - 1;
|
||||
if (childrenLength === 1) {
|
||||
if (__DEV__) {
|
||||
validateChildKeys(children);
|
||||
}
|
||||
this.props.children = children;
|
||||
} else if (childrenLength > 1) {
|
||||
var childArray = Array(childrenLength);
|
||||
for (var i = 0; i < childrenLength; i++) {
|
||||
if (__DEV__) {
|
||||
validateChildKeys(arguments[i + 1]);
|
||||
}
|
||||
childArray[i] = arguments[i + 1];
|
||||
}
|
||||
this.props.children = childArray;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -230,7 +349,7 @@ var ReactComponent = {
|
||||
*/
|
||||
mountComponent: function(rootID, transaction) {
|
||||
invariant(
|
||||
this._lifeCycleState === ComponentLifeCycle.UNMOUNTED,
|
||||
!this.isMounted(),
|
||||
'mountComponent(%s, ...): Can only mount an unmounted component.',
|
||||
rootID
|
||||
);
|
||||
@@ -255,14 +374,14 @@ var ReactComponent = {
|
||||
*/
|
||||
unmountComponent: function() {
|
||||
invariant(
|
||||
this._lifeCycleState === ComponentLifeCycle.MOUNTED,
|
||||
this.isMounted(),
|
||||
'unmountComponent(): Can only unmount a mounted component.'
|
||||
);
|
||||
var props = this.props;
|
||||
if (props.ref != null) {
|
||||
ReactOwner.removeComponentAsRefFrom(this, props.ref, props[OWNER]);
|
||||
}
|
||||
this._rootNode = null;
|
||||
ReactID.purgeID(this._rootNodeID);
|
||||
this._rootNodeID = null;
|
||||
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
|
||||
},
|
||||
@@ -279,20 +398,62 @@ var ReactComponent = {
|
||||
*/
|
||||
receiveProps: function(nextProps, transaction) {
|
||||
invariant(
|
||||
this._lifeCycleState === ComponentLifeCycle.MOUNTED,
|
||||
this.isMounted(),
|
||||
'receiveProps(...): Can only update a mounted component.'
|
||||
);
|
||||
this._pendingProps = nextProps;
|
||||
this._performUpdateIfNecessary(transaction);
|
||||
},
|
||||
|
||||
/**
|
||||
* Call `_performUpdateIfNecessary` within a new transaction.
|
||||
*
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @internal
|
||||
*/
|
||||
performUpdateIfNecessary: function() {
|
||||
var transaction = ReactComponent.ReactReconcileTransaction.getPooled();
|
||||
transaction.perform(this._performUpdateIfNecessary, this, transaction);
|
||||
ReactComponent.ReactReconcileTransaction.release(transaction);
|
||||
},
|
||||
|
||||
/**
|
||||
* If `_pendingProps` is set, update the component.
|
||||
*
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @internal
|
||||
*/
|
||||
_performUpdateIfNecessary: function(transaction) {
|
||||
if (this._pendingProps == null) {
|
||||
return;
|
||||
}
|
||||
var prevProps = this.props;
|
||||
this.props = this._pendingProps;
|
||||
this._pendingProps = null;
|
||||
this.updateComponent(transaction, prevProps);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the component's currently mounted representation.
|
||||
*
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {object} prevProps
|
||||
* @internal
|
||||
*/
|
||||
updateComponent: function(transaction, prevProps) {
|
||||
var props = this.props;
|
||||
// If either the owner or a `ref` has changed, make sure the newest owner
|
||||
// has stored a reference to `this`, and the previous owner (if different)
|
||||
// has forgotten the reference to `this`.
|
||||
if (nextProps[OWNER] !== props[OWNER] || nextProps.ref !== props.ref) {
|
||||
if (props.ref != null) {
|
||||
ReactOwner.removeComponentAsRefFrom(this, props.ref, props[OWNER]);
|
||||
if (props[OWNER] !== prevProps[OWNER] || props.ref !== prevProps.ref) {
|
||||
if (prevProps.ref != null) {
|
||||
ReactOwner.removeComponentAsRefFrom(
|
||||
this, prevProps.ref, prevProps[OWNER]
|
||||
);
|
||||
}
|
||||
// Correct, even if the owner is the same, and only the ref has changed.
|
||||
if (nextProps.ref != null) {
|
||||
ReactOwner.addComponentAsRefTo(this, nextProps.ref, nextProps[OWNER]);
|
||||
if (props.ref != null) {
|
||||
ReactOwner.addComponentAsRefTo(this, props.ref, props[OWNER]);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -302,18 +463,20 @@ var ReactComponent = {
|
||||
*
|
||||
* @param {string} rootID DOM ID of the root node.
|
||||
* @param {DOMElement} container DOM element to mount into.
|
||||
* @param {boolean} shouldReuseMarkup If true, do not insert markup
|
||||
* @final
|
||||
* @internal
|
||||
* @see {ReactMount.renderComponent}
|
||||
*/
|
||||
mountComponentIntoNode: function(rootID, container) {
|
||||
mountComponentIntoNode: function(rootID, container, shouldReuseMarkup) {
|
||||
var transaction = ReactComponent.ReactReconcileTransaction.getPooled();
|
||||
transaction.perform(
|
||||
this._mountComponentIntoNode,
|
||||
this,
|
||||
rootID,
|
||||
container,
|
||||
transaction
|
||||
transaction,
|
||||
shouldReuseMarkup
|
||||
);
|
||||
ReactComponent.ReactReconcileTransaction.release(transaction);
|
||||
},
|
||||
@@ -322,14 +485,27 @@ var ReactComponent = {
|
||||
* @param {string} rootID DOM ID of the root node.
|
||||
* @param {DOMElement} container DOM element to mount into.
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {boolean} shouldReuseMarkup If true, do not insert markup
|
||||
* @final
|
||||
* @private
|
||||
*/
|
||||
_mountComponentIntoNode: function(rootID, container, transaction) {
|
||||
_mountComponentIntoNode: function(
|
||||
rootID,
|
||||
container,
|
||||
transaction,
|
||||
shouldReuseMarkup) {
|
||||
invariant(
|
||||
container && container.nodeType === 1,
|
||||
'mountComponentIntoNode(...): Target container is not a DOM element.'
|
||||
);
|
||||
var renderStart = Date.now();
|
||||
var markup = this.mountComponent(rootID, transaction);
|
||||
ReactMount.totalInstantiationTime += (Date.now() - renderStart);
|
||||
|
||||
if (shouldReuseMarkup) {
|
||||
return;
|
||||
}
|
||||
|
||||
var injectionStart = Date.now();
|
||||
// Asynchronously inject markup by ensuring that the container is not in
|
||||
// the document when settings its `innerHTML`.
|
||||
@@ -375,30 +551,26 @@ var ReactComponent = {
|
||||
*/
|
||||
isOwnedBy: function(owner) {
|
||||
return this.props[OWNER] === owner;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets another component, that shares the same owner as this one, by ref.
|
||||
*
|
||||
* @param {string} ref of a sibling Component.
|
||||
* @return {?ReactComponent} the actual sibling Component.
|
||||
* @final
|
||||
* @internal
|
||||
*/
|
||||
getSiblingByRef: function(ref) {
|
||||
var owner = this.props[OWNER];
|
||||
if (!owner || !owner.refs) {
|
||||
return null;
|
||||
}
|
||||
return owner.refs[ref];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function logDeprecated(msg) {
|
||||
if (__DEV__) {
|
||||
throw new Error(msg);
|
||||
} else {
|
||||
console && console.warn && console.warn(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
ReactComponent.Mixin.update = function(props) {
|
||||
logDeprecated('this.update() is deprecated. Use this.setProps()');
|
||||
this.setProps(props);
|
||||
};
|
||||
ReactComponent.Mixin.updateAll = function(props) {
|
||||
logDeprecated('this.updateAll() is deprecated. Use this.replaceProps()');
|
||||
this.replaceProps(props);
|
||||
};
|
||||
|
||||
module.exports = ReactComponent;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ReactComponentWithPureRender
|
||||
* @providesModule ReactComponentWithPureRenderMixin
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -27,9 +27,10 @@
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var ReactComponentWithPureRender = require('ReactComponentWithPureRender');
|
||||
* var ReactComponentWithPureRender =
|
||||
* require('ReactComponentWithPureRenderMixin');
|
||||
* React.createClass({
|
||||
* mixins: [ReactComponentWithPureRender],
|
||||
* mixins: [ReactComponentWithPureRenderMixin],
|
||||
*
|
||||
* render: function() {
|
||||
* return <div className={this.props.className}>foo</div>;
|
||||
@@ -41,7 +42,7 @@
|
||||
* differences. Only mixin to components which have simple props and state, or
|
||||
* use `forceUpdate()` when you know deep data structures have changed.
|
||||
*/
|
||||
var ReactComponentWithPureRender = {
|
||||
var ReactComponentWithPureRenderMixin = {
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return !shallowEqual(this.props, nextProps) ||
|
||||
!shallowEqual(this.state, nextState);
|
||||
@@ -74,4 +75,4 @@ function shallowEqual(objA, objB) {
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = ReactComponentWithPureRender;
|
||||
module.exports = ReactComponentWithPureRenderMixin;
|
||||
|
||||
@@ -22,6 +22,7 @@ var ReactComponent = require('ReactComponent');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactOwner = require('ReactOwner');
|
||||
var ReactPropTransferer = require('ReactPropTransferer');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
@@ -80,17 +81,29 @@ var ReactCompositeComponentInterface = {
|
||||
mixins: SpecPolicy.DEFINE_MANY,
|
||||
|
||||
/**
|
||||
* Definition of props for this component.
|
||||
* Definition of prop types for this component.
|
||||
*
|
||||
* @type {array}
|
||||
* @type {object}
|
||||
* @optional
|
||||
*/
|
||||
props: SpecPolicy.DEFINE_ONCE,
|
||||
propTypes: SpecPolicy.DEFINE_ONCE,
|
||||
|
||||
|
||||
|
||||
// ==== Definition methods ====
|
||||
|
||||
/**
|
||||
* Invoked when the component is mounted. Values in the mapping will be set on
|
||||
* `this.props` if that prop is not specified (i.e. using an `in` check).
|
||||
*
|
||||
* This method is invoked before `getInitialState` and therefore cannot rely
|
||||
* on `this.state` or use `this.setState`.
|
||||
*
|
||||
* @return {object}
|
||||
* @optional
|
||||
*/
|
||||
getDefaultProps: SpecPolicy.DEFINE_ONCE,
|
||||
|
||||
/**
|
||||
* Invoked once before the component is mounted. The return value will be used
|
||||
* as the initial value of `this.state`.
|
||||
@@ -265,11 +278,53 @@ var RESERVED_SPEC_KEYS = {
|
||||
}
|
||||
}
|
||||
},
|
||||
props: function(Constructor, props) {
|
||||
Constructor.propDeclarations = props;
|
||||
propTypes: function(Constructor, propTypes) {
|
||||
Constructor.propTypes = propTypes;
|
||||
}
|
||||
};
|
||||
|
||||
function validateMethodOverride(proto, name) {
|
||||
var specPolicy = ReactCompositeComponentInterface[name];
|
||||
|
||||
// Disallow overriding of base class methods unless explicitly allowed.
|
||||
if (ReactCompositeComponentMixin.hasOwnProperty(name)) {
|
||||
invariant(
|
||||
specPolicy === SpecPolicy.OVERRIDE_BASE,
|
||||
'ReactCompositeComponentInterface: You are attempting to override ' +
|
||||
'`%s` from your class specification. Ensure that your method names ' +
|
||||
'do not overlap with React methods.',
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
// Disallow defining methods more than once unless explicitly allowed.
|
||||
if (proto.hasOwnProperty(name)) {
|
||||
invariant(
|
||||
specPolicy === SpecPolicy.DEFINE_MANY,
|
||||
'ReactCompositeComponentInterface: You are attempting to define ' +
|
||||
'`%s` on your component more than once. This conflict may be due ' +
|
||||
'to a mixin.',
|
||||
name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function validateLifeCycleOnReplaceState(instance) {
|
||||
var compositeLifeCycleState = instance._compositeLifeCycleState;
|
||||
invariant(
|
||||
instance.isMounted() ||
|
||||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
|
||||
'replaceState(...): Can only update a mounted or mounting component.'
|
||||
);
|
||||
invariant(
|
||||
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
|
||||
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
|
||||
'replaceState(...): Cannot update while unmounting component or during ' +
|
||||
'an existing state transition (such as within `render`).'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom version of `mixInto` which handles policy validation and reserved
|
||||
* specification keys when building `ReactCompositeComponent` classses.
|
||||
@@ -277,58 +332,44 @@ var RESERVED_SPEC_KEYS = {
|
||||
function mixSpecIntoComponent(Constructor, spec) {
|
||||
var proto = Constructor.prototype;
|
||||
for (var name in spec) {
|
||||
if (!spec.hasOwnProperty(name)) {
|
||||
var property = spec[name];
|
||||
if (!spec.hasOwnProperty(name) || !property) {
|
||||
continue;
|
||||
}
|
||||
var property = spec[name];
|
||||
var specPolicy = ReactCompositeComponentInterface[name];
|
||||
|
||||
// Disallow overriding of base class methods unless explicitly allowed.
|
||||
if (ReactCompositeComponentMixin.hasOwnProperty(name)) {
|
||||
invariant(
|
||||
specPolicy === SpecPolicy.OVERRIDE_BASE,
|
||||
'ReactCompositeComponentInterface: You are attempting to override ' +
|
||||
'`%s` from your class specification. Ensure that your method names ' +
|
||||
'do not overlap with React methods.',
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
// Disallow using `React.autoBind` on internal methods.
|
||||
if (specPolicy != null) {
|
||||
invariant(
|
||||
!property || !property.__reactAutoBind,
|
||||
'ReactCompositeComponentInterface: You are attempting to use ' +
|
||||
'`React.autoBind` on `%s`, a method that is internal to React.' +
|
||||
'Internal methods are called with the component as the context.',
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
// Disallow defining methods more than once unless explicitly allowed.
|
||||
if (proto.hasOwnProperty(name)) {
|
||||
invariant(
|
||||
specPolicy === SpecPolicy.DEFINE_MANY,
|
||||
'ReactCompositeComponentInterface: You are attempting to define ' +
|
||||
'`%s` on your component more than once. This conflict may be due ' +
|
||||
'to a mixin.',
|
||||
name
|
||||
);
|
||||
}
|
||||
validateMethodOverride(proto, name);
|
||||
|
||||
if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) {
|
||||
RESERVED_SPEC_KEYS[name](Constructor, property);
|
||||
} else if (property && property.__reactAutoBind) {
|
||||
if (!proto.__reactAutoBindMap) {
|
||||
proto.__reactAutoBindMap = {};
|
||||
}
|
||||
proto.__reactAutoBindMap[name] = property.__reactAutoBind;
|
||||
} else if (proto.hasOwnProperty(name)) {
|
||||
// For methods which are defined more than once, call the existing methods
|
||||
// before calling the new property.
|
||||
proto[name] = createChainedFunction(proto[name], property);
|
||||
} else {
|
||||
proto[name] = property;
|
||||
// Setup methods on prototype:
|
||||
// The following member methods should not be automatically bound:
|
||||
// 1. Expected ReactCompositeComponent methods (in the "interface").
|
||||
// 2. Overridden methods (that were mixed in).
|
||||
var isCompositeComponentMethod = name in ReactCompositeComponentInterface;
|
||||
var isInherited = name in proto;
|
||||
var markedDontBind = property.__reactDontBind;
|
||||
var isFunction = typeof property === 'function';
|
||||
var shouldAutoBind =
|
||||
isFunction &&
|
||||
!isCompositeComponentMethod &&
|
||||
!isInherited &&
|
||||
!markedDontBind;
|
||||
|
||||
if (shouldAutoBind) {
|
||||
if (!proto.__reactAutoBindMap) {
|
||||
proto.__reactAutoBindMap = {};
|
||||
}
|
||||
proto.__reactAutoBindMap[name] = property;
|
||||
proto[name] = property;
|
||||
} else {
|
||||
if (isInherited) {
|
||||
// For methods which are defined more than once, call the existing
|
||||
// methods before calling the new property.
|
||||
proto[name] = createChainedFunction(proto[name], property);
|
||||
} else {
|
||||
proto[name] = property;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,13 +383,9 @@ function mixSpecIntoComponent(Constructor, spec) {
|
||||
* @private
|
||||
*/
|
||||
function createChainedFunction(one, two) {
|
||||
return function chainedFunction(a, b, c, d, e, tooMany) {
|
||||
invariant(
|
||||
typeof tooMany === 'undefined',
|
||||
'Chained function can only take a maximum of 5 arguments.'
|
||||
);
|
||||
one.call(this, a, b, c, d, e);
|
||||
two.call(this, a, b, c, d, e);
|
||||
return function chainedFunction() {
|
||||
one.apply(this, arguments);
|
||||
two.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -357,7 +394,26 @@ function createChainedFunction(one, two) {
|
||||
* `this._compositeLifeCycleState` (which can be null).
|
||||
*
|
||||
* This is different from the life cycle state maintained by `ReactComponent` in
|
||||
* `this._lifeCycleState`.
|
||||
* `this._lifeCycleState`. The following diagram shows how the states overlap in
|
||||
* time. There are times when the CompositeLifeCycle is null - at those times it
|
||||
* is only meaningful to look at ComponentLifeCycle alone.
|
||||
*
|
||||
* Top Row: ReactComponent.ComponentLifeCycle
|
||||
* Low Row: ReactComponent.CompositeLifeCycle
|
||||
*
|
||||
* +-------+------------------------------------------------------+--------+
|
||||
* | UN | MOUNTED | UN |
|
||||
* |MOUNTED| | MOUNTED|
|
||||
* +-------+------------------------------------------------------+--------+
|
||||
* | ^--------+ +------+ +------+ +------+ +--------^ |
|
||||
* | | | | | | | | | | | |
|
||||
* | 0--|MOUNTING|-0-|RECEIV|-0-|RECEIV|-0-|RECEIV|-0-| UN |--->0 |
|
||||
* | | | |PROPS | | PROPS| | STATE| |MOUNTING| |
|
||||
* | | | | | | | | | | | |
|
||||
* | | | | | | | | | | | |
|
||||
* | +--------+ +------+ +------+ +------+ +--------+ |
|
||||
* | | | |
|
||||
* +-------+------------------------------------------------------+--------+
|
||||
*/
|
||||
var CompositeLifeCycle = keyMirror({
|
||||
/**
|
||||
@@ -396,12 +452,24 @@ var ReactCompositeComponentMixin = {
|
||||
* @internal
|
||||
*/
|
||||
construct: function(initialProps, children) {
|
||||
ReactComponent.Mixin.construct.call(this, initialProps, children);
|
||||
// Children can be either an array or more than one argument
|
||||
ReactComponent.Mixin.construct.apply(this, arguments);
|
||||
this.state = null;
|
||||
this._pendingState = null;
|
||||
this._compositeLifeCycleState = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether or not this composite component is mounted.
|
||||
* @return {boolean} True if mounted, false otherwise.
|
||||
* @protected
|
||||
* @final
|
||||
*/
|
||||
isMounted: function() {
|
||||
return ReactComponent.Mixin.isMounted.call(this) &&
|
||||
this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the component, renders markup, and registers event listeners.
|
||||
*
|
||||
@@ -413,14 +481,10 @@ var ReactCompositeComponentMixin = {
|
||||
*/
|
||||
mountComponent: function(rootID, transaction) {
|
||||
ReactComponent.Mixin.mountComponent.call(this, rootID, transaction);
|
||||
|
||||
// Unset `this._lifeCycleState` until after this method is finished.
|
||||
this._lifeCycleState = ReactComponent.LifeCycle.UNMOUNTED;
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
|
||||
|
||||
if (this.constructor.propDeclarations) {
|
||||
this._assertValidProps(this.props);
|
||||
}
|
||||
this._defaultProps = this.getDefaultProps ? this.getDefaultProps() : null;
|
||||
this._processProps(this.props);
|
||||
|
||||
if (this.__reactAutoBindMap) {
|
||||
this._bindAutoBindMethods();
|
||||
@@ -428,6 +492,7 @@ var ReactCompositeComponentMixin = {
|
||||
|
||||
this.state = this.getInitialState ? this.getInitialState() : null;
|
||||
this._pendingState = null;
|
||||
this._pendingForceUpdate = false;
|
||||
|
||||
if (this.componentWillMount) {
|
||||
this.componentWillMount();
|
||||
@@ -439,17 +504,15 @@ var ReactCompositeComponentMixin = {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.componentDidMount) {
|
||||
transaction.getReactOnDOMReady().enqueue(this, this.componentDidMount);
|
||||
}
|
||||
|
||||
this._renderedComponent = this._renderValidatedComponent();
|
||||
|
||||
// Done with mounting, `setState` will now trigger UI changes.
|
||||
this._compositeLifeCycleState = null;
|
||||
this._lifeCycleState = ReactComponent.LifeCycle.MOUNTED;
|
||||
|
||||
return this._renderedComponent.mountComponent(rootID, transaction);
|
||||
var markup = this._renderedComponent.mountComponent(rootID, transaction);
|
||||
if (this.componentDidMount) {
|
||||
transaction.getReactOnDOMReady().enqueue(this, this.componentDidMount);
|
||||
}
|
||||
return markup;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -465,6 +528,8 @@ var ReactCompositeComponentMixin = {
|
||||
}
|
||||
this._compositeLifeCycleState = null;
|
||||
|
||||
this._defaultProps = null;
|
||||
|
||||
ReactComponent.Mixin.unmountComponent.call(this);
|
||||
this._renderedComponent.unmountComponent();
|
||||
this._renderedComponent = null;
|
||||
@@ -479,33 +544,6 @@ var ReactCompositeComponentMixin = {
|
||||
// TODO: this.state = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the rendered DOM nodes given a new set of props.
|
||||
*
|
||||
* @param {object} nextProps Next set of properties.
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @final
|
||||
* @internal
|
||||
*/
|
||||
receiveProps: function(nextProps, transaction) {
|
||||
if (this.constructor.propDeclarations) {
|
||||
this._assertValidProps(nextProps);
|
||||
}
|
||||
ReactComponent.Mixin.receiveProps.call(this, nextProps, transaction);
|
||||
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
|
||||
if (this.componentWillReceiveProps) {
|
||||
this.componentWillReceiveProps(nextProps, transaction);
|
||||
}
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
|
||||
// When receiving props, calls to `setState` by `componentWillReceiveProps`
|
||||
// will set `this._pendingState` without triggering a re-render.
|
||||
var nextState = this._pendingState || this.state;
|
||||
this._pendingState = null;
|
||||
this._receivePropsAndState(nextProps, nextState, transaction);
|
||||
this._compositeLifeCycleState = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a subset of the state. Always use this or `replaceState` to mutate
|
||||
* state. You should treat `this.state` as immutable.
|
||||
@@ -513,13 +551,22 @@ var ReactCompositeComponentMixin = {
|
||||
* There is no guarantee that `this.state` will be immediately updated, so
|
||||
* accessing `this.state` after calling this method may return the old value.
|
||||
*
|
||||
* There is no guarantee that calls to `setState` will run synchronously,
|
||||
* as they may eventually be batched together. You can provide an optional
|
||||
* callback that will be executed when the call to setState is actually
|
||||
* completed.
|
||||
*
|
||||
* @param {object} partialState Next partial state to be merged with state.
|
||||
* @param {?function} callback Called after state is updated.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
setState: function(partialState) {
|
||||
setState: function(partialState, callback) {
|
||||
// Merge with `_pendingState` if it exists, otherwise with existing state.
|
||||
this.replaceState(merge(this._pendingState || this.state, partialState));
|
||||
this.replaceState(
|
||||
merge(this._pendingState || this.state, partialState),
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -530,60 +577,89 @@ var ReactCompositeComponentMixin = {
|
||||
* accessing `this.state` after calling this method may return the old value.
|
||||
*
|
||||
* @param {object} completeState Next state.
|
||||
* @param {?function} callback Called after state is updated.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
replaceState: function(completeState) {
|
||||
var compositeLifeCycleState = this._compositeLifeCycleState;
|
||||
invariant(
|
||||
this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED ||
|
||||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
|
||||
'replaceState(...): Can only update a mounted (or mounting) component.'
|
||||
);
|
||||
invariant(
|
||||
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
|
||||
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
|
||||
'replaceState(...): Cannot update while unmounting component or during ' +
|
||||
'an existing state transition (such as within `render`).'
|
||||
);
|
||||
|
||||
replaceState: function(completeState, callback) {
|
||||
validateLifeCycleOnReplaceState(this);
|
||||
this._pendingState = completeState;
|
||||
|
||||
// Do not trigger a state transition if we are in the middle of mounting or
|
||||
// receiving props because both of those will already be doing this.
|
||||
if (compositeLifeCycleState !== CompositeLifeCycle.MOUNTING &&
|
||||
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_PROPS) {
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
|
||||
|
||||
var nextState = this._pendingState;
|
||||
this._pendingState = null;
|
||||
|
||||
var transaction = ReactComponent.ReactReconcileTransaction.getPooled();
|
||||
transaction.perform(
|
||||
this._receivePropsAndState,
|
||||
this,
|
||||
this.props,
|
||||
nextState,
|
||||
transaction
|
||||
);
|
||||
ReactComponent.ReactReconcileTransaction.release(transaction);
|
||||
|
||||
this._compositeLifeCycleState = null;
|
||||
}
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Receives next props and next state, and negotiates whether or not the
|
||||
* component should update as a result.
|
||||
* Processes props by setting default values for unspecified props and
|
||||
* asserting that the props are valid.
|
||||
*
|
||||
* @param {object} nextProps Next object to set as props.
|
||||
* @param {?object} nextState Next object to set as state.
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {object} props
|
||||
* @private
|
||||
*/
|
||||
_receivePropsAndState: function(nextProps, nextState, transaction) {
|
||||
if (!this.shouldComponentUpdate ||
|
||||
_processProps: function(props) {
|
||||
var propName;
|
||||
var defaultProps = this._defaultProps;
|
||||
for (propName in defaultProps) {
|
||||
if (!(propName in props)) {
|
||||
props[propName] = defaultProps[propName];
|
||||
}
|
||||
}
|
||||
var propTypes = this.constructor.propTypes;
|
||||
if (propTypes) {
|
||||
var componentName = this.constructor.displayName;
|
||||
for (propName in propTypes) {
|
||||
var checkProp = propTypes[propName];
|
||||
if (checkProp) {
|
||||
checkProp(props, propName, componentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
performUpdateIfNecessary: function() {
|
||||
var compositeLifeCycleState = this._compositeLifeCycleState;
|
||||
// Do not trigger a state transition if we are in the middle of mounting or
|
||||
// receiving props because both of those will already be doing this.
|
||||
if (compositeLifeCycleState === CompositeLifeCycle.MOUNTING ||
|
||||
compositeLifeCycleState === CompositeLifeCycle.RECEIVING_PROPS) {
|
||||
return;
|
||||
}
|
||||
ReactComponent.Mixin.performUpdateIfNecessary.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* If any of `_pendingProps`, `_pendingState`, or `_pendingForceUpdate` is
|
||||
* set, update the component.
|
||||
*
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @internal
|
||||
*/
|
||||
_performUpdateIfNecessary: function(transaction) {
|
||||
if (this._pendingProps == null &&
|
||||
this._pendingState == null &&
|
||||
!this._pendingForceUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
var nextProps = this.props;
|
||||
if (this._pendingProps != null) {
|
||||
nextProps = this._pendingProps;
|
||||
this._processProps(nextProps);
|
||||
this._pendingProps = null;
|
||||
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
|
||||
if (this.componentWillReceiveProps) {
|
||||
this.componentWillReceiveProps(nextProps, transaction);
|
||||
}
|
||||
}
|
||||
|
||||
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
|
||||
|
||||
var nextState = this._pendingState || this.state;
|
||||
this._pendingState = null;
|
||||
|
||||
if (this._pendingForceUpdate ||
|
||||
!this.shouldComponentUpdate ||
|
||||
this.shouldComponentUpdate(nextProps, nextState)) {
|
||||
this._pendingForceUpdate = false;
|
||||
// Will set `this.props` and `this.state`.
|
||||
this._performComponentUpdate(nextProps, nextState, transaction);
|
||||
} else {
|
||||
@@ -592,6 +668,8 @@ var ReactCompositeComponentMixin = {
|
||||
this.props = nextProps;
|
||||
this.state = nextState;
|
||||
}
|
||||
|
||||
this._compositeLifeCycleState = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -614,7 +692,7 @@ var ReactCompositeComponentMixin = {
|
||||
this.props = nextProps;
|
||||
this.state = nextState;
|
||||
|
||||
this.updateComponent(transaction);
|
||||
this.updateComponent(transaction, prevProps, prevState);
|
||||
|
||||
if (this.componentDidUpdate) {
|
||||
transaction.getReactOnDOMReady().enqueue(
|
||||
@@ -631,16 +709,17 @@ var ReactCompositeComponentMixin = {
|
||||
* Sophisticated clients may wish to override this.
|
||||
*
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {object} prevProps
|
||||
* @param {?object} prevState
|
||||
* @internal
|
||||
* @overridable
|
||||
*/
|
||||
updateComponent: function(transaction) {
|
||||
updateComponent: function(transaction, prevProps, prevState) {
|
||||
ReactComponent.Mixin.updateComponent.call(this, transaction, prevProps);
|
||||
var currentComponent = this._renderedComponent;
|
||||
var nextComponent = this._renderValidatedComponent();
|
||||
if (currentComponent.constructor === nextComponent.constructor) {
|
||||
if (!nextComponent.props.isStatic) {
|
||||
currentComponent.receiveProps(nextComponent.props, transaction);
|
||||
}
|
||||
currentComponent.receiveProps(nextComponent.props, transaction);
|
||||
} else {
|
||||
// These two IDs are actually the same! But nothing should rely on that.
|
||||
var thisID = this._rootNodeID;
|
||||
@@ -665,28 +744,42 @@ var ReactCompositeComponentMixin = {
|
||||
* This will not invoke `shouldUpdateComponent`, but it will invoke
|
||||
* `componentWillUpdate` and `componentDidUpdate`.
|
||||
*
|
||||
* @param {?function} callback Called after update is complete.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
forceUpdate: function() {
|
||||
var transaction = ReactComponent.ReactReconcileTransaction.getPooled();
|
||||
transaction.perform(
|
||||
this._performComponentUpdate,
|
||||
this,
|
||||
this.props,
|
||||
this.state,
|
||||
transaction
|
||||
forceUpdate: function(callback) {
|
||||
var compositeLifeCycleState = this._compositeLifeCycleState;
|
||||
invariant(
|
||||
this.isMounted() ||
|
||||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
|
||||
'forceUpdate(...): Can only force an update on mounted or mounting ' +
|
||||
'components.'
|
||||
);
|
||||
ReactComponent.ReactReconcileTransaction.release(transaction);
|
||||
invariant(
|
||||
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
|
||||
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
|
||||
'forceUpdate(...): Cannot force an update while unmounting component ' +
|
||||
'or during an existing state transition (such as within `render`).'
|
||||
);
|
||||
this._pendingForceUpdate = true;
|
||||
ReactUpdates.enqueueUpdate(this, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_renderValidatedComponent: function() {
|
||||
var renderedComponent;
|
||||
ReactCurrentOwner.current = this;
|
||||
var renderedComponent = this.render();
|
||||
ReactCurrentOwner.current = null;
|
||||
try {
|
||||
renderedComponent = this.render();
|
||||
} catch (error) {
|
||||
// IE8 requires `catch` in order to use `finally`.
|
||||
throw error;
|
||||
} finally {
|
||||
ReactCurrentOwner.current = null;
|
||||
}
|
||||
invariant(
|
||||
ReactComponent.isValidComponent(renderedComponent),
|
||||
'%s.render(): A valid ReactComponent must be returned.',
|
||||
@@ -695,21 +788,6 @@ var ReactCompositeComponentMixin = {
|
||||
return renderedComponent;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @private
|
||||
*/
|
||||
_assertValidProps: function(props) {
|
||||
var propDeclarations = this.constructor.propDeclarations;
|
||||
var componentName = this.constructor.displayName;
|
||||
for (var propName in propDeclarations) {
|
||||
var checkProp = propDeclarations[propName];
|
||||
if (checkProp) {
|
||||
checkProp(props, propName, componentName);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -731,29 +809,34 @@ var ReactCompositeComponentMixin = {
|
||||
*/
|
||||
_bindAutoBindMethod: function(method) {
|
||||
var component = this;
|
||||
var hasWarned = false;
|
||||
function autoBound(a, b, c, d, e, tooMany) {
|
||||
invariant(
|
||||
typeof tooMany === 'undefined',
|
||||
'React.autoBind(...): Methods can only take a maximum of 5 arguments.'
|
||||
);
|
||||
if (component._lifeCycleState === ReactComponent.LifeCycle.MOUNTED) {
|
||||
return method.call(component, a, b, c, d, e);
|
||||
} else if (!hasWarned) {
|
||||
hasWarned = true;
|
||||
if (__DEV__) {
|
||||
var boundMethod = function() {
|
||||
return method.apply(component, arguments);
|
||||
};
|
||||
if (__DEV__) {
|
||||
var componentName = component.constructor.displayName;
|
||||
var _bind = boundMethod.bind;
|
||||
boundMethod.bind = function(newThis) {
|
||||
// User is trying to bind() an autobound method; we effectively will
|
||||
// ignore the value of "this" that the user is trying to use, so
|
||||
// let's warn.
|
||||
if (newThis !== component) {
|
||||
console.warn(
|
||||
'React.autoBind(...): Attempted to invoke an auto-bound method ' +
|
||||
'on an unmounted instance of `%s`. You either have a memory leak ' +
|
||||
'or an event handler that is being run after unmounting.',
|
||||
component.constructor.displayName || 'ReactCompositeComponent'
|
||||
'bind(): React component methods may only be bound to the ' +
|
||||
'component instance. See ' + componentName
|
||||
);
|
||||
} else if (arguments.length === 1) {
|
||||
console.warn(
|
||||
'bind(): You are binding a component method to the component. ' +
|
||||
'React does this for you automatically in a high-performance ' +
|
||||
'way, so you can safely remove this call. See ' + componentName
|
||||
);
|
||||
return boundMethod;
|
||||
}
|
||||
}
|
||||
return _bind.apply(boundMethod, arguments);
|
||||
};
|
||||
}
|
||||
return autoBound;
|
||||
return boundMethod;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var ReactCompositeComponentBase = function() {};
|
||||
@@ -784,9 +867,7 @@ var ReactCompositeComponent = {
|
||||
* @public
|
||||
*/
|
||||
createClass: function(spec) {
|
||||
var Constructor = function(initialProps, children) {
|
||||
this.construct(initialProps, children);
|
||||
};
|
||||
var Constructor = function() {};
|
||||
Constructor.prototype = new ReactCompositeComponentBase();
|
||||
Constructor.prototype.constructor = Constructor;
|
||||
mixSpecIntoComponent(Constructor, spec);
|
||||
@@ -794,9 +875,17 @@ var ReactCompositeComponent = {
|
||||
Constructor.prototype.render,
|
||||
'createClass(...): Class specification must implement a `render` method.'
|
||||
);
|
||||
// Reduce time spent doing lookups by setting these on the prototype.
|
||||
for (var methodName in ReactCompositeComponentInterface) {
|
||||
if (!Constructor.prototype[methodName]) {
|
||||
Constructor.prototype[methodName] = null;
|
||||
}
|
||||
}
|
||||
|
||||
var ConvenienceConstructor = function(props, children) {
|
||||
return new Constructor(props, children);
|
||||
var instance = new Constructor();
|
||||
instance.construct.apply(instance, arguments);
|
||||
return instance;
|
||||
};
|
||||
ConvenienceConstructor.componentConstructor = Constructor;
|
||||
ConvenienceConstructor.originalSpec = spec;
|
||||
@@ -804,33 +893,23 @@ var ReactCompositeComponent = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Marks the provided method to be automatically bound to the component.
|
||||
* This means the method's context will always be the component.
|
||||
*
|
||||
* React.createClass({
|
||||
* handleClick: React.autoBind(function() {
|
||||
* this.setState({jumping: true});
|
||||
* }),
|
||||
* render: function() {
|
||||
* return <a onClick={this.handleClick}>Jump</a>;
|
||||
* }
|
||||
* });
|
||||
* TODO: Delete this when all callers have been updated to rely on this
|
||||
* behavior being the default.
|
||||
*
|
||||
* Backwards compatible stub for what is now the default behavior.
|
||||
* @param {function} method Method to be bound.
|
||||
* @public
|
||||
*/
|
||||
autoBind: function(method) {
|
||||
function unbound() {
|
||||
invariant(
|
||||
false,
|
||||
'React.autoBind(...): Attempted to invoke an auto-bound method that ' +
|
||||
'was not correctly defined on the class specification.'
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
'React.autoBind() is now deprecated. All React component methods ' +
|
||||
'are auto bound by default, so React.autoBind() is a no-op. It ' +
|
||||
'will be removed in the next version of React'
|
||||
);
|
||||
}
|
||||
unbound.__reactAutoBind = method;
|
||||
return unbound;
|
||||
return method;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactCompositeComponent;
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
*
|
||||
* The current owner is the component who should own any components that are
|
||||
* currently being constructed.
|
||||
*
|
||||
* The depth indicate how many composite components are above this render level.
|
||||
*/
|
||||
var ReactCurrentOwner = {
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ReactDOM
|
||||
* @typechecks
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
@@ -40,16 +40,17 @@ var objMapKeyVal = require('objMapKeyVal');
|
||||
* @private
|
||||
*/
|
||||
function createDOMComponentClass(tag, omitClose) {
|
||||
var Constructor = function(initialProps, children) {
|
||||
this.construct(initialProps, children);
|
||||
};
|
||||
|
||||
var Constructor = function() {};
|
||||
Constructor.prototype = new ReactNativeComponent(tag, omitClose);
|
||||
Constructor.prototype.constructor = Constructor;
|
||||
|
||||
return function(props, children) {
|
||||
return new Constructor(props, children);
|
||||
var ConvenienceConstructor = function(props, children) {
|
||||
var instance = new Constructor();
|
||||
instance.construct.apply(instance, arguments);
|
||||
return instance;
|
||||
};
|
||||
ConvenienceConstructor.componentConstructor = Constructor;
|
||||
return ConvenienceConstructor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,74 +63,118 @@ var ReactDOM = objMapKeyVal({
|
||||
a: false,
|
||||
abbr: false,
|
||||
address: false,
|
||||
area: false,
|
||||
article: false,
|
||||
aside: false,
|
||||
audio: false,
|
||||
b: false,
|
||||
base: false,
|
||||
bdi: false,
|
||||
bdo: false,
|
||||
big: false,
|
||||
blockquote: false,
|
||||
body: false,
|
||||
br: true,
|
||||
button: false,
|
||||
canvas: false,
|
||||
caption: false,
|
||||
cite: false,
|
||||
code: false,
|
||||
col: true,
|
||||
colgroup: false,
|
||||
data: false,
|
||||
datalist: false,
|
||||
dd: false,
|
||||
del: false,
|
||||
details: false,
|
||||
dfn: false,
|
||||
div: false,
|
||||
section: false,
|
||||
dl: false,
|
||||
dt: false,
|
||||
em: false,
|
||||
embed: true,
|
||||
fieldset: false,
|
||||
figcaption: false,
|
||||
figure: false,
|
||||
footer: false,
|
||||
// Danger: this gets monkeypatched! See ReactDOMForm for more info.
|
||||
form: false,
|
||||
form: false, // NOTE: Injected, see `ReactDOMForm`.
|
||||
h1: false,
|
||||
h2: false,
|
||||
h3: false,
|
||||
h4: false,
|
||||
h5: false,
|
||||
h6: false,
|
||||
head: false,
|
||||
header: false,
|
||||
hr: true,
|
||||
html: false,
|
||||
i: false,
|
||||
iframe: false,
|
||||
img: true,
|
||||
input: true,
|
||||
ins: false,
|
||||
kbd: false,
|
||||
keygen: true,
|
||||
label: false,
|
||||
legend: false,
|
||||
li: false,
|
||||
line: false,
|
||||
link: false,
|
||||
main: false,
|
||||
map: false,
|
||||
mark: false,
|
||||
menu: false,
|
||||
menuitem: false, // NOTE: Close tag should be omitted, but causes problems.
|
||||
meta: true,
|
||||
meter: false,
|
||||
nav: false,
|
||||
noscript: false,
|
||||
object: false,
|
||||
ol: false,
|
||||
optgroup: false,
|
||||
option: false,
|
||||
output: false,
|
||||
p: false,
|
||||
param: true,
|
||||
pre: false,
|
||||
progress: false,
|
||||
q: false,
|
||||
rp: false,
|
||||
rt: false,
|
||||
ruby: false,
|
||||
s: false,
|
||||
samp: false,
|
||||
script: false,
|
||||
section: false,
|
||||
select: false,
|
||||
small: false,
|
||||
source: false,
|
||||
span: false,
|
||||
sub: false,
|
||||
sup: false,
|
||||
strong: false,
|
||||
style: false,
|
||||
sub: false,
|
||||
summary: false,
|
||||
sup: false,
|
||||
table: false,
|
||||
tbody: false,
|
||||
td: false,
|
||||
textarea: false,
|
||||
textarea: false, // NOTE: Injected, see `ReactDOMTextarea`.
|
||||
tfoot: false,
|
||||
th: false,
|
||||
thead: false,
|
||||
time: false,
|
||||
title: false,
|
||||
tr: false,
|
||||
track: true,
|
||||
u: false,
|
||||
ul: false,
|
||||
'var': false,
|
||||
video: false,
|
||||
wbr: false,
|
||||
|
||||
// SVG
|
||||
circle: false,
|
||||
g: false,
|
||||
line: false,
|
||||
path: false,
|
||||
polyline: false,
|
||||
rect: false,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ReactDOMIDOperations
|
||||
* @typechecks
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
/*jslint evil: true */
|
||||
@@ -24,7 +24,7 @@
|
||||
var CSSPropertyOperations = require('CSSPropertyOperations');
|
||||
var DOMChildrenOperations = require('DOMChildrenOperations');
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var ReactDOMNodeCache = require('ReactDOMNodeCache');
|
||||
var ReactID = require('ReactID');
|
||||
|
||||
var getTextContentAccessor = require('getTextContentAccessor');
|
||||
var invariant = require('invariant');
|
||||
@@ -36,7 +36,6 @@ var invariant = require('invariant');
|
||||
* @private
|
||||
*/
|
||||
var INVALID_PROPERTY_ERRORS = {
|
||||
content: '`content` must be set using `updateTextContentByID()`.',
|
||||
dangerouslySetInnerHTML:
|
||||
'`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.',
|
||||
style: '`style` must be set using `updateStylesByID()`.'
|
||||
@@ -66,7 +65,7 @@ var ReactDOMIDOperations = {
|
||||
* @internal
|
||||
*/
|
||||
updatePropertyByID: function(id, name, value) {
|
||||
var node = ReactDOMNodeCache.getCachedNodeByID(id);
|
||||
var node = ReactID.getNode(id);
|
||||
invariant(
|
||||
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
|
||||
'updatePropertyByID(...): %s',
|
||||
@@ -75,6 +74,24 @@ var ReactDOMIDOperations = {
|
||||
DOMPropertyOperations.setValueForProperty(node, name, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates a DOM node to remove a property. This should only be used to remove
|
||||
* DOM properties in `DOMProperty`.
|
||||
*
|
||||
* @param {string} id ID of the node to update.
|
||||
* @param {string} name A property name to remove, see `DOMProperty`.
|
||||
* @internal
|
||||
*/
|
||||
deletePropertyByID: function(id, name, value) {
|
||||
var node = ReactID.getNode(id);
|
||||
invariant(
|
||||
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
|
||||
'updatePropertyByID(...): %s',
|
||||
INVALID_PROPERTY_ERRORS[name]
|
||||
);
|
||||
DOMPropertyOperations.deleteValueForProperty(node, name, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* This should almost never be used instead of `updatePropertyByID()` due to
|
||||
* the extra object allocation required by the API. That said, this is useful
|
||||
@@ -95,14 +112,15 @@ var ReactDOMIDOperations = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates a DOM node with new style values.
|
||||
* Updates a DOM node with new style values. If a value is specified as '',
|
||||
* the corresponding style property will be unset.
|
||||
*
|
||||
* @param {string} id ID of the node to update.
|
||||
* @param {object} styles Mapping from styles to values.
|
||||
* @internal
|
||||
*/
|
||||
updateStylesByID: function(id, styles) {
|
||||
var node = ReactDOMNodeCache.getCachedNodeByID(id);
|
||||
var node = ReactID.getNode(id);
|
||||
CSSPropertyOperations.setValueForStyles(node, styles);
|
||||
},
|
||||
|
||||
@@ -114,7 +132,7 @@ var ReactDOMIDOperations = {
|
||||
* @internal
|
||||
*/
|
||||
updateInnerHTMLByID: function(id, html) {
|
||||
var node = ReactDOMNodeCache.getCachedNodeByID(id);
|
||||
var node = ReactID.getNode(id);
|
||||
// HACK: IE8- normalize whitespace in innerHTML, removing leading spaces.
|
||||
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
|
||||
node.innerHTML = (html && html.__html || '').replace(/^ /g, ' ');
|
||||
@@ -128,7 +146,7 @@ var ReactDOMIDOperations = {
|
||||
* @internal
|
||||
*/
|
||||
updateTextContentByID: function(id, content) {
|
||||
var node = ReactDOMNodeCache.getCachedNodeByID(id);
|
||||
var node = ReactID.getNode(id);
|
||||
node[textContentAccessor] = content;
|
||||
},
|
||||
|
||||
@@ -141,9 +159,8 @@ var ReactDOMIDOperations = {
|
||||
* @see {Danger.dangerouslyReplaceNodeWithMarkup}
|
||||
*/
|
||||
dangerouslyReplaceNodeWithMarkupByID: function(id, markup) {
|
||||
var node = ReactDOMNodeCache.getCachedNodeByID(id);
|
||||
var node = ReactID.getNode(id);
|
||||
DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup);
|
||||
ReactDOMNodeCache.purgeEntireCache();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -151,14 +168,8 @@ var ReactDOMIDOperations = {
|
||||
* Detect if any elements were removed instead of blindly purging.
|
||||
*/
|
||||
manageChildrenByParentID: function(parentID, domOperations) {
|
||||
var parent = ReactDOMNodeCache.getCachedNodeByID(parentID);
|
||||
var parent = ReactID.getNode(parentID);
|
||||
DOMChildrenOperations.manageChildren(parent, domOperations);
|
||||
ReactDOMNodeCache.purgeEntireCache();
|
||||
},
|
||||
|
||||
setTextNodeValueAtIndexByParentID: function(parentID, index, value) {
|
||||
var parent = ReactDOMNodeCache.getCachedNodeByID(parentID);
|
||||
DOMChildrenOperations.setTextNodeValueAtIndex(parent, index, value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Copyright 2013 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ReactDOMNodeCache
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactMount = require('ReactMount');
|
||||
|
||||
var nodeCache = {};
|
||||
|
||||
/**
|
||||
* DOM node cache only intended for use by React. Placed into a shared module so
|
||||
* that both read and write utilities may benefit from a shared cache.
|
||||
*/
|
||||
var ReactDOMNodeCache = {
|
||||
/**
|
||||
* Releases fast id lookups (node/style cache). This implementation is
|
||||
* aggressive with purging because the bookkeeping associated with doing fine
|
||||
* grained deleted from the cache may outweight the benefits of the cache. The
|
||||
* heuristic that should be used to purge is 'any time anything is deleted'.
|
||||
* Typically this means that a large amount of content is being replaced and
|
||||
* several elements would need purging regardless. It's also a time when an
|
||||
* application is likely not in the middle of a "smooth operation" (such as
|
||||
* animating/scrolling).
|
||||
*/
|
||||
purgeEntireCache: function() {
|
||||
nodeCache = {};
|
||||
return nodeCache;
|
||||
},
|
||||
getCachedNodeByID: function(id) {
|
||||
return nodeCache[id] ||
|
||||
(nodeCache[id] =
|
||||
document.getElementById(id) ||
|
||||
ReactMount.findReactRenderedDOMNodeSlow(id));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ReactDOMNodeCache;
|
||||
@@ -20,9 +20,17 @@
|
||||
|
||||
var ReactDOM = require('ReactDOM');
|
||||
var ReactDOMForm = require('ReactDOMForm');
|
||||
var ReactDOMInput = require('ReactDOMInput');
|
||||
var ReactDOMOption = require('ReactDOMOption');
|
||||
var ReactDOMSelect = require('ReactDOMSelect');
|
||||
var ReactDOMTextarea = require('ReactDOMTextarea');
|
||||
|
||||
var DefaultDOMPropertyConfig = require('DefaultDOMPropertyConfig');
|
||||
var DOMProperty = require('DOMProperty');
|
||||
|
||||
var DefaultEventPluginOrder = require('DefaultEventPluginOrder');
|
||||
var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
|
||||
var ChangeEventPlugin = require('ChangeEventPlugin');
|
||||
var EventPluginHub = require('EventPluginHub');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var SimpleEventPlugin = require('SimpleEventPlugin');
|
||||
@@ -35,23 +43,24 @@ function inject() {
|
||||
EventPluginHub.injection.injectInstanceHandle(ReactInstanceHandles);
|
||||
|
||||
/**
|
||||
* Two important event plugins included by default (without having to require
|
||||
* Some important event plugins included by default (without having to require
|
||||
* them).
|
||||
*/
|
||||
EventPluginHub.injection.injectEventPluginsByName({
|
||||
'SimpleEventPlugin': SimpleEventPlugin,
|
||||
'EnterLeaveEventPlugin': EnterLeaveEventPlugin
|
||||
'EnterLeaveEventPlugin': EnterLeaveEventPlugin,
|
||||
'ChangeEventPlugin': ChangeEventPlugin
|
||||
});
|
||||
|
||||
/*
|
||||
* This is a bit of a hack. We need to override the <form> element
|
||||
* to be a composite component because IE8 does not bubble or capture
|
||||
* submit to the top level. In order to make this work with our
|
||||
* dependency graph we need to inject it here.
|
||||
*/
|
||||
ReactDOM.injection.injectComponentClasses({
|
||||
form: ReactDOMForm
|
||||
form: ReactDOMForm,
|
||||
input: ReactDOMInput,
|
||||
option: ReactDOMOption,
|
||||
select: ReactDOMSelect,
|
||||
textarea: ReactDOMTextarea
|
||||
});
|
||||
|
||||
DOMProperty.injection.injectDOMPropertyConfig(DefaultDOMPropertyConfig);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||