Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
React.js v0.13 changes and beyond
koba04
March 30, 2015
Programming
1
15k
React.js v0.13 changes and beyond
Gunosy React Meetup
http://gunosy-frontend.connpass.com/event/12636/
koba04
March 30, 2015
Tweet
Share
More Decks by koba04
See All by koba04
koba04
0
500
koba04
2
1.4k
koba04
9
7.8k
koba04
0
87
koba04
1
1.1k
koba04
4
13k
koba04
6
1k
koba04
2
1.7k
koba04
2
420
Other Decks in Programming
See All in Programming
grapecity_dev
0
190
grapecity_dev
1
180
mihyaeru21
0
350
bkuhlmann
4
660
manfredsteyer
PRO
0
180
grapecity_dev
0
180
7603
0
270
morimorihoge
1
140
manfredsteyer
PRO
0
120
takahi5
1
380
joergneumann
0
130
temoki
2
210
Featured
See All Featured
edds
56
9.4k
rasmusluckow
318
18k
malarkey
119
16k
jakevdp
774
200k
maggiecrowley
9
490
kneath
219
15k
addyosmani
311
21k
lauravandoore
437
28k
scottboms
251
11k
afnizarnur
176
14k
jrom
116
7.2k
malarkey
192
8.6k
Transcript
3FBDUKTWDIBOHFTBOECFZPOE (VOPTZ3FBDU.FFUVQ !LPCB
!LPCB w 8FC"QQMJDBUJPO&OHJOFFS w IUUQLPCBDPN w Ұਓ3FBDUKT"EWFOU$BMFBOEBS w IUUQRJJUBDPNBEWFOUDBMFOEBSSFBDUKT
None
None
3FBDUKTW
http://facebook.github.io/react/blog/2015/03/10/react-v0.13.html
http://blog.koba04.com/post/2015/03/05/react-js-v013-changes/
&4$MBTTFT
&4$MBTTFT class Hello extends React.Component { constructor(props) { super(props); this.state
= { count: 0 }; } static get propTypes() { title: React.PropTypes.string.isRequired } onClick() { this.setState({ count: this.state.count + 1 }) } render() { return ( <div> <p>{this.props.title}</p> <button onClick={this.onClick.bind(this)}>click</button> <p>{this.state.count}</p> </div> ); } }
&4$MBTTFT w HFU*OJUJBM4UBUFͰͳ͘ɺDPOTUSVDUPSͰTUBUFͷॳظΛࢦఆ w BVUPCJOEJOH͞Εͳ͘ͳͬͨ w NJYJOݱ࣌Ͱະαϙʔτ w IUUQTNFEJVNDPN!EBO@BCSBNPWNJYJOTBSFEFBEMPOHMJWFIJHIFS PSEFSDPNQPOFOUTBEGF
w HFU%0./PEFɺJT.PVOUFEɺSFQMBDF1SPQTɺ SFQMBDF4UBUFɺTFU1SPQTEFQSFDBUFE
1SPQJTJNNVUBCMF
1SPQJTJNNVUBCMF w 1SPQͷΛมߋ͠Α͏ͱ͢ΔͱXBSOJOH͕ग़ΔΑ͏ʹͳͬͨ w WͰ1SPQ͕*NNVUBCMFͰ͋Δ͜ͱΛલఏͱͨ͠ύϑΥʔϚ ϯεɾνϡʔχϯάΛ͢Δ͜ͱ͕త render() { // Warning:
Don't set .props.level of the React component. … var title = <title level={1} />; if (this.state.isImportant) title.props.level = 2; };
1SPQTJTJNNVUBCMF w TIPVME$PNQPOFOU6QEBUFͰมߋΛݕग़ग़དྷͳ͍ w UIJTQSPQTͱҾͰ͞ΕΔOFYU1SPQT͕ಉ͡ʹͳΔ w 3FBDUDSFBUF&MFNFOUҎޙมߋ͞Εͳ͍͜ͱΛอূ͍ͨ͠ w 1SPQ5ZQFTͷνΣοΫDSFBUF&MFNFOUͷ࣌ʹߦ͑Α͘ ͳΔ
w ωετͨ͠1SPQͷʹ͍ͭͯର֎ w ݅ʹΑͬͯ1SPQ͕ม͍͑ͨ߹ɺ3FBDUDSFBUF&MFNFOU ʹ͢1SPQΛ࡞Δ·Ͱʹ͍͍ͤ
1SPQJTJNNVUBCMF if (shouldUseFoo) { return <Foo foo={10} bar={true} />; }
else { return <Foo bar={false} />; } or var props = { bar: false }; if (shouldUseFoo) { props.foo = 10; props.bar = true; } return <Foo {...props} />;
TFU4UBUF
TFU4UBUFDBOUBLFBGVODUJPOBTTUBSHVNFOU w τϥϯβΫγϣϯ͕ඞཁͱ͞ΕΔσʔλͷߋ৽ʹ͏ w ͜Ε·Ͱ@QFOEJOH4UBUFΛ͏ඞཁ͕͋ͬͨ console.log(this.state.count) // 0 this.setState({count: this.state.count
+ 1}) this.setState({count: this.state.count + 1}) // state.count will render as 1 console.log(this.state.count) // 0 this.setState(function(state, props) { return {count: state.count + 1} }); this.setState(function(state, props) { return {count: state.count + 1} }); // state.count will render as 2
TFU4UBUFJTBMXBZTBTZODISPOPVT w ͜Ε·ͰॳճͷϚϯτ࣌ͷݺͼग़͠ಉظత͚ͩͬͨͲৗʹ ඇಉظͰ#BUDIVQEBUFʹͳΔ componentDidMount() { console.log(this.state.count) // 0 this.setState({
count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) console.log(this.state.count) ɹ// v0.12 is 2 ɹ// v0.13 is 0 }
TFU4UBUFPOVONPVOUFEDPNQPOFOU w ΞϯϚϯτ͞ΕͨDPNQPOFOUͰͷTFU4UBUF GPSDF6QEBUF ͷݺͼग़ͨ࣌͠ʹΤϥʔͰͳ͘XBSOJOH͕ग़ྗ͞ΕΔΑ͏ʹ w "KBYʹର͢ΔϨεϙϯεΛTFU4UBUF͢ΔΑ͏ͳͱ͖ʹ JT.PVOUFEͰϒϩοΫ͢Δඞཁ͕ͳ͘ͳͬͨ request(‘/api/users/koba04’, (res)
=> { // if (this.isMounted()) { this.setState({ user: res.body.user }); // } });
3FBDUpOE%0./PEF
pOE%0./PEFJOTUFBEPGHFU%0./PEF w HFU%0./PEF&4$MBTTFTͰఏڙ͞Εͯͳ͍͠ DSFBUF$MBTTͰXBSOJOH͕ग़ΔͷͰஔ͖͑Δ class Sample extends React.Component { onClick()
{ this.setState({ // text: this.refs.inputText.getDOMNode().value text: React.findDOMNode(this.refs.inputText).value }); } render() { return( <div> <input type=“text” ref=“inputText” /> <button onClick={this.onClick.bind(this)}>click</button> </div> ); } }
0XOFSDPOUFYUUP1BSFOU$POUFYU
0XOFSBOE1BSFOU w 1BSFOUͱ0XOFS͕Ұக͠ͳ͍DPOUFYUΛ͓͏ͱ͢Δͱ XBSOJOH͕ग़ΔΑ͏ʹͳͬͨ w 1BSFOUϕʔεͷDPOUFYU·࣮ͩ͞Ε͍ͯͳ͍ w 0XOFS 1BSFOU DPOUFYU
w VOEPDVNFOUFEGFBUVSF
0XOFSBOE1BSFOU const p = <p>title</p>; class Hoge extends React.Component {
render() { return ( <div> {p} <span>hello</span> </div> ); } } span: owner is Hoge, parent is div ! Hoge p: owner is null, parent is div ! Hoge
$POUFYU const Parent React.createClass({ static get childContextTypes() { return {
name: React.PropTypes.string } } getChildContext() { return { name: ‘parent’ } } render() { return <Child /> } } // Child render <GrandChild /> class GrandChild extends React.Component { static get contextTypes() { return { name: React.PropTypes.string } } render() { <div>{this.context.name}</div> } }
$POUFYU const child = <Child />; const Parent React.createClass({ static
get childContextTypes() { return { name: React.PropTypes.string } } getChildContext() { return { name: ‘parent’ } } render() { return <div>{child}</div> } } class Child extends React.Component { static get contextTypes() { return { name: React.PropTypes.string } } render() { <div>{this.context.name}</div> } } React.render(<Parent />, document.getElementById(‘app’)); // Warning: owner-based and parent-based contexts differ
SFBDUSPVUFSXJUIDPOUFYU // 0.12.x var Foo = React.createClass({ mixins: [ Router.State
], render: function () { var id = this.getParams().id; // etc. ... } }); // 0.13.x w/ ES6 fanciness class Foo extends React.Component { render () { var { router } = this.context; var id = router.getCurrentParams().id; // etc. } } Foo.contextTypes = { router: React.PropTypes.func };
SFGDBOQBTTBDBMMCBDL class Hello extends React.Component { onClick() { this.setState({ text:
React.findDOMNode(this._inputText).value }); } render() { return ( <div> <input type=“text” ref={(c) => this._inputText = c} /> <button onClick={this.onClick.bind(this)}>click</button> </div> ); } } w طଘͷSFGlYYYzͦͷ··͑Δ 0XOFSDPOUFYU
3FBDUDMPOF&MFNFOU
3FBDUDMPOF&MFNFOU w 3FBDUBEEPOTDMPOF8JUI1SPQTʹ͍ۙ w 1SPQͱ*NNVUBCMFͱͯ͠ѻ͏͜ͱͰDMPOF͢Δ໘͕૿͑Δ ͨΊɺίΞͷ"1*ʹͳͬͨ w ҧ͍ͱͯ͠TUZMFDMBTT/BNFͷNFSHFߦΘΕͣɺSFG͕อ ࣋͞ΕΔ var
newChildren = React.Children.map( this.props.children, child => React.cloneElement(child, { foo: true }) );
DIJMESFO
4VQQPSUJUFSBUPSTBTDIJMESFO w JUFSBUPSΛͦͷ··͢͜ͱ͕ग़དྷΔ w *NNVUBCMFKTͷ-JTUΛNBQͨ͠ͷΛࠓ·ͰUP"SSBZͨ͠Γ ͢Δඞཁ͕͋ͬͨͷ͕ͦͷ··ͤΔ render() { <ul> {Immutable.List([1,2,3]).map(i
=> <li key={i}>{i}</li>)} </ul> }
,FZFE0CKFDU
,FZFE0CKFDU w \LFZFMFNFOU^ΛDIJMESFOʹ͢ͱXBSOJOH͕ग़ΔΑ͏ʹͳͬ ͨ w Ճ͞Εͨ3FBDUBEEPOTDSFBUF'SBHNFOUΛͬͨΓNBQͰ ճͨ͠Γ͢Ε͍͍ // Keyed Object
<div>{ {a: <span />, b: <span />} }</div> // === <div><span key=“a” /><span key=“b” /></div> <div>{React.addons.createFragment({a: <span />, b: <span />})</div>
+49
UBSHFUPQUJPO w EFGBVMUFTɻ͜Ε·Ͱͷڍಈʹ͢ΔͳΒFTΛࢦఆ͢Δ w FT͕EFGBVMUʹͳΓͦ͏ͳྲྀΕ͚ͩͬͨͲɺ!TFCNDL͕FT ΛEFGBVMUʹ͢ΔΑ͏ʹ͍࣋ͬͯͬͨ
UBSHFUPQUJPO w &4DMBTTFTͰॻ͍ͨ࣌ͷϝιου͕FOVNFSBCMF͔Ͳ͏͔ͷ ҧ͍͕͋Δ class Hello extends React.Component { foo()
{ console.log("foo"); } render() { return <div>hello</div>; } }
UBSHFUFT var ____Class0=React.Component;for(var ____Class0____Key in ____Class0) {if(____Class0.hasOwnProperty(____Class0____Key)) {Hello[____Class0____Key]=____Class0[____Class0____Key];}}var ____SuperProtoOf____Class0=____Class0===null? null:____Class0.prototype;Hello.prototype=Object.create(____SuperProtoOf____C
lass0);Hello.prototype.constructor=Hello;Hello.__superConstructor__=____Class 0;function Hello(){"use strict”;if(____Class0!==null) {____Class0.apply(this,arguments);}} Object.defineProperty(Hello.prototype,"foo", {writable:true,configurable:true,value:function() {"use strict"; console.log("foo"); }}); Object.defineProperty(Hello.prototype,"render", {writable:true,configurable:true,value:function() {"use strict"; return React.createElement("div", null, "hello"); }});
UBSHFUFT var ____Class0=React.Component;for(var ____Class0____Key in ____Class0) {if(____Class0.hasOwnProperty(____Class0____Key)) {Hello[____Class0____Key]=____Class0[____Class0____Key];}}var ____SuperProtoOf____Class0=____Class0===null? null:____Class0.prototype;Hello.prototype=Object.create(____SuperProtoOf____C
lass0);Hello.prototype.constructor=Hello;Hello.__superConstructor__=____Class 0;function Hello(){"use strict”;if(____Class0!==null) {____Class0.apply(this,arguments);}} Hello.prototype.foo=function() {"use strict"; console.log("foo"); }; Hello.prototype.render=function() {"use strict"; return React.createElement("div", null, "hello"); };
w ͱ^͕Ҏલจࣈྻͱͯ͠ѻΘΕ͍͚ͯͨͲQBSTFΤϥʔʹ ͳΔΑ͏ʹͳͬͨ BOE^BSFUSFBUFEBTBQBSTFFSSPS render() { return <div>10 > 2</div>;
// parse error! }
4IBMMPXSFOEFSJOH EPDVNFOUFE
4IBMMPXSFOEFSJOH w ֊·ͰΛSFOEFSͯ݁͠ՌΛฦ͢5FTU6UJMT w OPEFڥͰಈ࡞͢Δ w SFOEFSϝιουͷ݁ՌΛ֬ೝ͍ͨ͠ͱ͖ʹศར
4IBMMPXSFOEFSJOH class Parent extends React.Component { render() { return <div><Child
name=“child” /></div>; } } class Child extends React.Component { render() { return <div>{this.props.name}</div>; } } const shallowRenderer = React.addons.TestUtils.createRenderer(); shallowRenderer.render(<Parent />); const result = shallowRenderer.getRenderOutput(); console.assert(result.type === 'div'); console.assert(result.props.children.type === Child); console.assert(result.props.children.props.name === 'child'); console.assert(result.props.children.props.children === undefined); ➜ babel-node test.js ➜
3FBDUKTW 1MBO
3FVTF$POTUBOU7BMVF5ZQFT w ಉ͡3FBDU&MFNFOUΛ͍·Θ͢͜ͱͰEJ⒎ͷίετΛݮΒ͢ w SFOEFSԿݺΕͯͦͷʹ3FBDU&MFNFOU͕࡞ΒΕΔ function render() { return <div
className="foo" />; } // ͜͏ͬͯίϯύΠϧ͢Δ(Πϝʔδ) var div = React.createElement("div", {className: “foo”}); function render() { return div; }
5BHHJOH3FBDU&MFNFOUT w 3FBDU&MFNFOUʹUBH͚Λͯ͠ɺEJ⒎ΞϧΰϦζϜΛ࠷దԽ͢ Δ <div className="foo" style={{ width: w, height:
5 }}>{c}</div> // ͜͏ͬͯίϯύΠϧ͢Δ(Πϝʔδ) { __t: 7, type: 'div', props: { className: 'foo', style: { width: w, height: 5 }, children: c } } // ·ͨ var t = { className: 1, style: { height: 1 } }; { __t: t, type: 'div', props: { className: 'foo', style: { width: w, height: 5 }, children: c } }
*OMJOF3FBDU&MFNFOUT w QSPEVDUJPOϏϧυͷ͚࣌ͩɺ3FBDUDSFBUF&MFNFOUͰͳ͘ ͯJOMJOFPCKFDUʹม͢Δ͜ͱͰίετΛݮΒ͢ w DSFBUF&MFNFOU࣌ͷ1SPQ5ZQFTʹΑΔνΣοΫͰ͖ͳ͘ͳΔ ͷͰQSPEVDUJPOϏϧυͷ͚࣌ͩ <div className="foo">{bar}<Baz key="baz"
/></div> // ͜͏ͬͯίϯύΠϧ͢Δ(Πϝʔδ) { type: 'div', props: { className: 'foo', children: [ bar, { type: Baz, props: { }, key: 'baz', ref: null } ] }, key: null, ref: null }
None
3FBDUKTW
0CTFSWBCMF"1* w IUUQTHJUIVCDPNGBDFCPPLSFBDUJTTVFT class Foo { observe() { return {
user: loadUser(this.props.userID) }; } render() { if (this.data.user.id !== this.props.userID) { // Ensure that we never show inconsistent userID / user.name combinations. return <Spinner />; } return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>; } }
3FBDU—1BSTF w IUUQCMPHQBSTFDPNQBSTFBOESFBDUTIBSFEDIFNJTUSZ // Render a list of comments from
the Parse API var CommentBlock = React.createClass({ mixins: [ParseReact.Mixin], observe: function() { return { comments: (new Parse.Query('Comment')).descending('createdAt') }; }, render: function() { return <ul> {this.data.comments.map(function(c) { return <li>{c.text}</li> })} </ul>; } });
7FSTJPOJOH
7FSTJPOJOH w ݱࡏ w 9 ͰEFQSFDBUJPOXBSOJOHTɺ9 Ͱ"1*আ w Ҡߦ w
9:ͰEFQSFDBUJPOXBSOJOHTɺ9 Ͱ"1*আ w IUUQTHJTUHJUIVCDPN[QBPFFFGDFBG
3FBDUKTW
http://facebook.github.io/react/blog/2014/03/28/the-road-to-1.0.html
3FBDUKTW w ͢ͰʹQSPEVDUJPOSFBEZ w $POUFYUͲ͏͢Δ͔ w "EEPOTͷཧɾ֎෦ϥΠϒϥϦԽ w "OJNBUJPOͷվળ w
ʜ
5IBOLZPV TQFBLFSEFDLDPNLPCB