Upgrade to Pro — share decks privately, control downloads, hide ads and more …

React.js v0.13 changes and beyond

45daf58c77e9dbbab5a1c8a5afc7ac5c?s=47 koba04
March 30, 2015

React.js v0.13 changes and beyond

45daf58c77e9dbbab5a1c8a5afc7ac5c?s=128

koba04

March 30, 2015
Tweet

Transcript

  1. 3FBDUKTWDIBOHFTBOECFZPOE (VOPTZ3FBDU.FFUVQ   !LPCB

  2. !LPCB w 8FC"QQMJDBUJPO&OHJOFFS w IUUQLPCBDPN w Ұਓ3FBDUKT"EWFOU$BMFBOEBS w IUUQRJJUBDPNBEWFOUDBMFOEBSSFBDUKT

  3. None
  4. None
  5. 3FBDUKTW

  6. http://facebook.github.io/react/blog/2015/03/10/react-v0.13.html

  7. http://blog.koba04.com/post/2015/03/05/react-js-v013-changes/

  8. &4$MBTTFT

  9. &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> ); } }
  10. &4$MBTTFT w HFU*OJUJBM4UBUFͰ͸ͳ͘ɺDPOTUSVDUPSͰTUBUFͷॳظ஋Λࢦఆ w BVUPCJOEJOH͞Εͳ͘ͳͬͨ w NJYJO͸ݱ࣌఺Ͱະαϙʔτ w IUUQTNFEJVNDPN!EBO@BCSBNPWNJYJOTBSFEFBEMPOHMJWFIJHIFS PSEFSDPNQPOFOUTBEGF

    w HFU%0./PEFɺJT.PVOUFEɺSFQMBDF1SPQTɺ SFQMBDF4UBUFɺTFU1SPQT͸EFQSFDBUFE
  11. 1SPQJTJNNVUBCMF

  12. 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; };
  13. 1SPQTJTJNNVUBCMF w TIPVME$PNQPOFOU6QEBUFͰมߋΛݕग़ग़དྷͳ͍ w UIJTQSPQTͱҾ਺Ͱ౉͞ΕΔOFYU1SPQT͕ಉ͡ʹͳΔ w 3FBDUDSFBUF&MFNFOUҎޙ͸มߋ͞Εͳ͍͜ͱΛอূ͍ͨ͠ w 1SPQ5ZQFTͷνΣοΫ΋DSFBUF&MFNFOUͷ࣌ʹߦ͑͹Α͘ ͳΔ

    w ωετͨ͠1SPQͷ஋ʹ͍ͭͯ͸ର৅֎ w ৚݅ʹΑͬͯ1SPQ͕ม͍͑ͨ৔߹͸ɺ3FBDUDSFBUF&MFNFOU ʹ౉͢1SPQΛ࡞Δ·Ͱʹ౉ͤ͹͍͍
  14. 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} />;
  15. TFU4UBUF

  16. 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
  17. 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 }
  18. 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 }); // } });
  19. 3FBDUpOE%0./PEF

  20. 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> ); } }
  21. 0XOFSDPOUFYUUP1BSFOU$POUFYU

  22. 0XOFSBOE1BSFOU w 1BSFOUͱ0XOFS͕Ұக͠ͳ͍DPOUFYUΛ࢖͓͏ͱ͢Δͱ XBSOJOH͕ग़ΔΑ͏ʹͳͬͨ w 1BSFOUϕʔεͷDPOUFYU͸·࣮ͩ૷͞Ε͍ͯͳ͍ w 0XOFS 1BSFOU DPOUFYU

     w VOEPDVNFOUFEGFBUVSF
  23. 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
  24. $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> } }
  25. $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
  26. 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 };
  27. 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
  28. 3FBDUDMPOF&MFNFOU

  29. 3FBDUDMPOF&MFNFOU w 3FBDUBEEPOTDMPOF8JUI1SPQTʹ͍ۙ w 1SPQͱ*NNVUBCMFͱͯ͠ѻ͏͜ͱͰDMPOF͢Δ৔໘͕૿͑Δ ͨΊɺίΞͷ"1*ʹͳͬͨ w ҧ͍ͱͯ͠͸TUZMF΍DMBTT/BNFͷNFSHF͸ߦΘΕͣɺSFG͕อ ࣋͞ΕΔ var

    newChildren = React.Children.map( this.props.children, child => React.cloneElement(child, { foo: true }) );
  30. DIJMESFO

  31. 4VQQPSUJUFSBUPSTBTDIJMESFO w JUFSBUPSΛͦͷ··౉͢͜ͱ͕ग़དྷΔ w *NNVUBCMFKTͷ-JTUΛNBQͨ͠΋ͷΛࠓ·Ͱ͸UP"SSBZͨ͠Γ ͢Δඞཁ͕͋ͬͨͷ͕ͦͷ··౉ͤΔ render() { <ul> {Immutable.List([1,2,3]).map(i

    => <li key={i}>{i}</li>)} </ul> }
  32. ,FZFE0CKFDU

  33. ,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>
  34. +49

  35. UBSHFUPQUJPO w EFGBVMU͸FTɻ͜Ε·Ͱͷڍಈʹ͢ΔͳΒFTΛࢦఆ͢Δ w FT͕EFGBVMUʹͳΓͦ͏ͳྲྀΕ͚ͩͬͨͲɺ!TFCNDL͕FT ΛEFGBVMUʹ͢ΔΑ͏ʹ͍࣋ͬͯͬͨ

  36. UBSHFUPQUJPO w &4DMBTTFTͰॻ͍ͨ࣌ͷϝιου͕FOVNFSBCMF͔Ͳ͏͔ͷ ҧ͍͕͋Δ class Hello extends React.Component { foo()

    { console.log("foo"); } render() { return <div>hello</div>; } }
  37. 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"); }});
  38. 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"); };
  39. w ͱ^͕Ҏલ͸จࣈྻͱͯ͠ѻΘΕ͍͚ͯͨͲQBSTFΤϥʔʹ ͳΔΑ͏ʹͳͬͨ BOE^BSFUSFBUFEBTBQBSTFFSSPS render() { return <div>10 > 2</div>;

    // parse error! }
  40. 4IBMMPXSFOEFSJOH EPDVNFOUFE

  41. 4IBMMPXSFOEFSJOH w ֊૚·ͰΛSFOEFSͯ݁͠ՌΛฦ͢5FTU6UJMT w OPEF؀ڥͰ΋ಈ࡞͢Δ w SFOEFSϝιουͷ݁ՌΛ֬ೝ͍ͨ͠ͱ͖ʹศར

  42. 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 ➜
  43. 3FBDUKTW  1MBO

  44. 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; }
  45. 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 } }
  46. *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 }
  47. None
  48. 3FBDUKTW

  49. 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>; } }
  50. 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>; } });
  51. 7FSTJPOJOH

  52. 7FSTJPOJOH w ݱࡏ w 9 ͰEFQSFDBUJPOXBSOJOHTɺ9 Ͱ"1*࡟আ w Ҡߦ w

    9:ͰEFQSFDBUJPOXBSOJOHTɺ9 Ͱ"1*࡟আ w IUUQTHJTUHJUIVCDPN[QBPFFFGDFBG
  53. 3FBDUKTW

  54. http://facebook.github.io/react/blog/2014/03/28/the-road-to-1.0.html

  55. 3FBDUKTW w ͢ͰʹQSPEVDUJPOSFBEZ w $POUFYUͲ͏͢Δ͔ w "EEPOTͷ੔ཧɾ֎෦ϥΠϒϥϦԽ w "OJNBUJPOͷվળ w

    ʜ
  56. 5IBOLZPV TQFBLFSEFDLDPNLPCB