The state of React.js 2016

The state of React.js 2016

A slide for html5j #65
http://eventdots.jp/event/589181

2016/07/27: Updated about React.PureComponent

45daf58c77e9dbbab5a1c8a5afc7ac5c?s=128

koba04

May 30, 2016
Tweet

Transcript

  1. 5IFTUBUFPG3FBDUKT IUNMKWPM   !LPCB

  2. None
  3. w XJMMCFIFMEJO+VOF "3FBDUKTNFFUVQPSHBOJ[FS

  4. 3FBDU&VSPQFJO1BSJT✈

  5. 8IPVTFT3FBDUKT

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

  7. *OTUBHSBN

  8. /FUqJY

  9. 5XJUUFS NPCJMF

  10. BJSCOC

  11. VCFS

  12. 3FBDUKTJTBCBUUMFUFTUFEMJCSBSZ GBDFCPPLVTFT3FBDUPONBTUFSCSBODIBUGBDFCPPLDPN

  13. 5IFIJTUPSZPG3FBDUKT

  14. 2013 2014 2015 2016 Flux v0.13 ReactNative for iOS GraphQL

    Redux Relay React Native for Android v0.14 React Native for Windows v15.0.0 …
  15. 8IZ3FBDU

  16. w *UIJOLUIFSFBTPONBOZQFPQMFVTF3FBDUJT/05B QFSGPSNBODF w 'PSQSFEJDUBCMF6*NBOBHFNFOUCZl%FDMBSBUJWFBOEQVSF $PNQPOFOUzBOEl&YQMJDJUEBUBqPXzXJUIMFTT QFSGPSNBODFEFHSBEBUJPO w 1SPWJEJOHFTDBQFIBUDIFT 8IZ3FBDU

  17. $PNQPOFOU

  18. w :PVDBODSFBUFNPTUDPNQPOFOUTBT4UBUFMFTT'VODUJPO $PNQPOFOUT 4'$  w 4'$EPFTO`UIBTBCBDLJOHJOTUBODF w /PTUBUF /PMJGFDZDMFNFUIPET

    /PSFGT w 1FSGPQUJNJTBUJPOTGPS4'$XJMMCFJOUIFGVUVSF 4UBUFMFTT'VODUJPO$PNQPOFOUT const Item = ({item}) => ( <div> <div>{item.name}×{item.count}</div> </div> ); // <Item item={{name: ‘foo’, count: 1}} />
  19. %PZPVOFFETUBUFTPSMJGFDZDMF NFUIPETJOZPVSDPNQPOFOUT  ↪︎&4$MBTTFT$PNQPOFOUT

  20. &4$MBTTFT class Counter extends React.Component { constructor(…args) {
 super(…args); this.state

    = { count: 0 }; this.onClick = this.onClick.bind(this); } onClick() { this.setState({ count: this.state.count + 1 }); } render() { return ( <div> <button onClick={this.onClick}>++<button> <div>{this.state.count}</div> </div> ); }
  21. w 4VQQPSUJOHNJOJNVN"1*T w *UEPFTO`UTVQQPSUSFQMBDF4UBUF JT.PVOUFE w .JYJOTBSFEFBE -POHMJWF)JHI0SEFS$PNQPOFOUT )0$ &4$MBTTFT$PNQPOFUT

    import shallowCompare from ‘react-addons-shallow-compare’; const pure = Component => class PureComponent extends React.Component { shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this, nextProps, nextState); } render () { return <Component {…this.props} /> } }; const Item = ({item}) => <div>{item.name}×{item.count}</div>; const PureItem = pure(Item);
  22. w 3FBDUDSFBUF$MBTTGFFMTPVUEBUFE w *UIBTGFBUVSFTUIBUJTDPOWFOJFOUGPSEFWFMPQFST w NJYJOH‎)JHI0SEFS$PNQPOFOUT w BVUPCJOEJOH‎&4$MBTT'JFMET4UBUJD1SPQFSUJFT  w

    *UNBZCFDPNFBTFQBSBUFQBDLBHF 3FBDUDSFBUF$MBTT class Button extends React.Component { onClick = () => this.setState({count: this.state.count + 1}); } Stage1
  23. DPSFOPUFT.BZ

  24. w "TJNQMFCFODINBSLGPS&4$MBTTFT XJUI TIPVME$PNQPOFOU6QEBUF WT4'$ w IUUQTKTpEEMFOFULPCBKNL &4$MBTTFTWT4'$

  25. "EE3FBDU1VSF$PNQPOFOU JOIFSJUQVSJUZGPS GVODUJPOBMDPNQPOFOUT WIP PureComponent was merged as #7195. It’s

    a PureRenderMixin for ES Classes.
  26. "EE3FBDU1VSF$PNQPOFOU JOIFSJUQVSJUZGPS GVODUJPOBMDPNQPOFOUT class Post extends React.PureComponent { // or

    React.Component render() { return ( <div className="post"> <PostHeader model={this.props.model} /> <PostBody model={this.props.model} /> </div> ); } } function PostHeader(props) { // ... } function PostBody(props) { // ... } Pure Pure This wasn’t merged
  27. "QJUGBMMPGTIPVME$PNQPOFOU6QEBUF import shallowCompare from ‘react-addons-shallow-compare’; class Item extends React.Component {

    shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this, nextProps, nextState); } render() { return ( <div> <p>{this.props.name}</p> <button onClick={this.props.onClick}>click</button> </div> ); } } <Item name=“foo” onClick={() => console.log(‘click’)} />
  28. SFBDUBEEPOTQFSG w 1FSGIBTSFXSJUUFOCZ%BO"CSBNPWBUW w QSJOU%0.SFOBNFEUPQSJOU0QFSBUJPOT w :PVDBO import Perf from

    ‘react-addons-perf’; Perf.start(); ReactDOM.render(<App name="React" />, el); setTimeout(() => { ReactDOM.render(<App name="React" />, el); Perf.stop(); Perf.printWasted(); }, 1000);
  29. XIZEJEZPVVQEBUF w IUUQTHJUIVCDPNHBSCMFTXIZEJEZPVVQEBUF w zJGEFpOFB$PNQPOFOUIBTDPNQPOFOU%JE6QEBUF UIF XIZEJEZPVVQEBUFXJMMOPUXPSLz import React from

    ‘react’; import {whyDidYouUpdate} from 'why-did-you-update'; whyDidYouUpdate(React); // monkey patches for React
  30. 1SPQT

  31. w 1SPQTJTJNNVUBCMF Wʙ  w 0CKFDUGSFF[F!EFW Wʙ  w "EEFE3FBDUDMPOF&MFNFOU

    Wʙ  w TFU1SPQTBOESFQMBDF1SPQTIBWFHPOF W  w 1SPQ5ZQFTJTBMFHBDZGFBUVSF w :PVDBOVTF'MPXPS5ZQF4DSJQUJOTUFBE JNDPNQBUJCMF  w *UNBZCFDPNFBTFQBSBUFEQBDLBHF 1SPQT
  32. DPSFOPUFT.BZ

  33. 4UBUF

  34. w TFU4UBUFCFIBWFTBTCBUDIFE6QEBUFTJO&WFOU-JTUFOFS BOEMJGFDZDMFNFUIPET w :PVDBOQBTTBGVODUJPOUPTFU4UBUFGPSUSBOTBDUJPOBM VQEBUFT Wʙ 4UBUF onClick() {

    this.setState((state, props) => ({count: state.count + 1})); this.setState((state, props) => ({count: state.count + 1})); }
  35. w UIFGPMMPXJOHFYBNQMF SFOEFSPDDVSTUXPUJNFT w /PCBUDIFE6QEBUFT "QJUGBMMPGCBUDIFE6QEBUFT onClick() { fetch(API_URL) .then(res

    => res.json()) .then(json => { this.setState({user: json.user}); // … this.setState({completed: true}); }); }
  36. w 3FBDU%0.VOTUBCMF@CBUDIFE6QEBUFTQSPWJEFT CBUDIFE6QEBUFT:PVDBOBWPJEVOOFDFTTBSZSFOEFST w *U`TVOTUBCMF#VUUIF"1*JTVTFECZ3FMBZFUD VOTUBCMF@CBUDIFE6QEBUFT onClick() { fetch(API_URL) .then(res

    => res.json()) .then(json => { ReactDOM.unstable_batchedUpdates(() => { this.setState({user: json.user}); // … this.setState({completed: true}); }); }); }
  37. 3FBDU&MFNFOU

  38. w 3FBDUDSFBUF&MFNFOUSFUVSOTQMBJOPCKFDU Wʙ  w /PNPSFTQBOBOEEBUBSFBDUJE FYDFQU443  Wʙ 3FBDU&MFNFOU

    // <div>Hello {name}</div> <div data-reactid=".0"> <span data-reactid=".0.0">Hello </span> <span data-reactid=".0.1">React</span> </div> <div data-reactroot> <!-- react-text: 2 -->Hello <!-- /react-text --> <!-- react-text: 3 -->React<!-- /react-text --> </div>
  39. 3FG%0.

  40. w 4USJOH3FGJTMFHBDZ:PVTIPVMEVTFDBMMCBDL3FG w %0.$PNQPOFOUTSFUVSOJUT%0./PEFBT3FG w HFU%0./PEFTIBTHPOF W  w 3FBDU%0.pOE%0./PEFTSFUVSOTB%0./PEFGSPN

    $PNQPTJUF$PNQPOFOUT 3FG%0. render() { let text; return ( <div> <input type=“text” ref={c => c && c.focus()} /> <input type=“text” ref={c => text = c} /> </div> ); }
  41. $POUFYU

  42. w $POUFYUCFDPNFTBEPDVNFOUFEGFBUVSF w IUUQGBDFCPPLHJUIVCJPSFBDUEPDTDPOUFYUIUNM w *U`TBGFBUVSFGPS3FBDUMJCSBSJFT $POUFYU const Item =

    ({item}, {settings}) => ( <div className={settings.theme}>{item.name}</div> ); Item.contextTypes = { settings: React.PropTypes.object };
  43. 5FTU

  44. w 8FDBOHFU4IBMMPX3FOEFSFS W  w :PVDBOUFTUGPS3FBDU$PNQPOFOUTPO/PEF FOWJSPONFOU w *UEPFTO`UTVQQPSU3FGBOEMJGFDZDMFNFUIPETDPNQMFUFMZ w

    *U`TBl4IBMMPXzSFOEFSJOH 5FTU const shallowRenderer = TestUtils.createRenderer(); const elementTree = shallowRenderer.render(<YourComponent />); assert(elementTree.props.children[0].type === ‘div’);
  45. w 5FTUJOHVUJMJUJFTGPS3FBDU$PNQPOFOUT w TIBMMPX NPVOU SFOEFS w :PVDBOUFTUGPS3FBDU$PNQPOFOUFBTJMZBOE NBJOUBJOBCMF BJSCOCFO[ZNF

    import {shallow} from ‘enzyme’; const wrapper = shallow(<YourComponent />); assert(wrapper.find(Link).prop(‘to’) === ‘/foo’); wrapper.find(‘button’).simulate(‘click’);
  46. w %PFT&O[ZNFCFDPNFUIFP⒏DJBM5FTU6UJMT BJSCOCFO[ZNF core-notes May 05.

  47. &4-JOU3FBDU

  48. w &4-JOUIFMQTZPVUPDSFBUF3FBDU$PNQPOFOUT w FTMJOUQMVHJOSFBDU w 3FBDUTQFDJpDMJOUJOHSVMFTGPS&4-JOU w IUUQTHJUIVCDPNZBOOJDLDSFTMJOUQMVHJOSFBDU w FTMJOUQMVHJOKTYBZ

    w 4UBUJD"45DIFDLFSGPSBDDFTTJCJMJUZSVMFTPO+49FMFNFOUT w IUUQTHJUIVCDPNFWDPIFOFTMJOUQMVHJOKTYBZ &4-JOU3FBDU
  49. &4-JOU3FBDU

  50. &4-JOU3FBDU w OPTUSJOHSFGT w QSFGFSFTDMBTT w QSFGFSTUBUFMFTTGVODUJPO w OPEJSFDUNVUBUJPOTUBUF w

    TPSUDPNQ w KTYOPCJOE w KTYLFZ
  51. 3FBDU/BUJWF

  52. w 3FBDU/BUJWFIBTBIVHFFDPTZTUFN w 'BDFCPPLJTVTJOH3FBDU/BUJWFJO'BDFCPPLBQQ 3FBDU/BUJWF

  53. 3FBDU/BUJWF

  54. w 3FBDU/BUJWFJTKVTUBSFOEFSFS 3FBDU/BUJWF import React, {Component} from 'react'; import {TabBarIOS,

    NavigatorIOS} from ‘react-native’; class App extends Component { render() { return ( <TabBarIOS> <TabBarIOS.Item title="React Native" selected={true}> <NavigatorIOS initialRoute={{ title: 'React Native' }} /> </TabBarIOS.Item> </TabBarIOS> ); } }
  55. w 3FBDU/BUJWFJTKVTUBSFOEFSFS 3FBDU/BUJWF (facebook/react) src !"" isomorphic # !"" children

    # !"" classic # $"" modern !"" renderers # !"" art # !"" dom # # !"" client # # !"" server # # $"" shared # !"" native # !"" noop # $"" shared !"" shared
  56. NBLFJUPQFODPN

  57. "SDIJUFDUVSF

  58. w IUUQTHJUIVCDPNSFBDUKTSFBDUCBTJD SFBDUCBTJT

  59. w IUUQTHJUIVCDPNJBNEVTUBOUJOZSFBDUSFOEFSFS w "NJOJNVNJNQMFNFOUBUJPOPG3FBDU3FOEFSFS 5JOZ3FBDU3FOEFSFS

  60. 'VUVSF 

  61. w :PVTIPVMEXBUDISFBDUKTDPSFOPUFT w 3FBDUXJMMSFMFBTFOFXNJOPSPSQBUDIWFSTJPOTFWFSZUXP XFFLT w 5PBEPQU&NCFSMJLF3'$QSPDFTT  w &YQFSJNFOUJOHXJUI4UZMF4IFFUDSFBUF

    POUIFXFC  w /FX3FDPODJMFS  w 3FBDU$PSFJTNPSFTJNQMFS 'VUVSF 
  62. <8*1>/FX$PSF"MHPSJUIN

  63. /FX$PSF"MHPSJUIN

  64. /FX3FDPODJMFS*OGSB

  65. <'JCFS>$IJME3FDPODJMFS /FX$PSPVUJOFT 1SJNJUJWF

  66. w 4DIFEVMJOH3FBDUSFDVSTJWFSFOEFSJOH  w $VSSFOUMZ SFDVSTJWFSFOEFSJOHJTTZODISPOPVT XIJDIJTB SFBTPO3FBDUJTTMPX <8*1>/FX3FDPODJMFS //

    src/renderers/noop/ReactNoop.js var NoopRenderer = ReactFiberReconciler({ createHostInstance() { }, scheduleHighPriCallback(callback) { scheduledHighPriCallback = callback; }, scheduleLowPriCallback(callback) { scheduledLowPriCallback = callback; },
  67. <8*1>/FX3FDPODJMFS function Continuation({ isSame }) { return <span>{isSame ? 'foo==bar'

    : 'foo!=bar'}</span>; } function Child({ bar }) { return ReactCoroutine.createYield({ bar: bar, }, Continuation, null); } function Indirection() { return [<Child bar={true} />, <Child bar={false} />]; } function HandleYields(props, yields) { return yields.map(y => <y.continuation isSame={props.foo === y.props.bar} /> ); } function Parent(props) { return ReactCoroutine.createCoroutine( props.children, HandleYields, props ); } function App() { return <div><Parent foo={true}><Indirection /></Parent></div>; } ReactNoop.render(<App />); ReactNoop.flush();
  68. <8*1>/FX3FDPODJMFS

  69. <8*1>/FX3FDPODJMFS w 3FTPVSDFT w IUUQTHJUIVCDPNSFBDUKTSFBDUCBTJD w IUUQTHJUIVCDPNSFBDUKTSFBDUGVUVSFCMPCNBTUFS -BZPVUQSPUPUZQFJOEFYKT

  70. 5IBOLZPV TQFBLFSEFDLDPNLPCB