Controllable React Components

Controllable React Components

Slides from a presentation about react-controllables.

Originally given at ReactDC on 4/7/2015

523b00c3c60e634c61e7b121a66b3902?s=128

Eric Eldredge

April 07, 2015
Tweet

Transcript

  1. With react-controllables Controllable React Components

  2. HZDG.COM LETTERTWO ERIC ELDREDGE LETTERTWO@GMAIL.COM HZDG PHOTO MISSING

  3. The Tao of the React Component Props vs State

  4. When do you use props vs. state?

  5. Always use props… • Enables composition • Keeps your React

    Components pure
  6. …unless you need state • Some components are affected by

    user interactions • Since props are immutable, interactions that change the component must either be handled by the parent component, or translated to state
  7. React Form Components <input type="text" value="Hey Dude!" /> Controlled: Uncontrolled:

    Uncontrolled (with a default value): <input type="text" onChange={handler} /> <input type="text" defaultValue="Hey Dude!" onChange={handler} />
  8. Interactive Components • Controlled Components receive values as props •

    Uncontrolled Components keep values in state
  9. The best of both worlds • An interactive component maintains

    its own state • Sometimes it yields control to its parent (props) • It can be both Controlled and Uncontrolled
  10. The best of both worlds: react-controllables MATTHEWWITHANM MATTHEW DAPENA-TRETTER github.com/matthewwithanm/react-controllables

  11. Example: TabBar • When you click on a tab, it

    becomes selected • The other tabs in the bar become deselected • The selected tab is stored as state in the component
  12. class TabBar extends React.Component { constructor() { super(); this.state =

    {selectedIndex: 0}; } handleClick(event) { const el = event.target; const index = el.parentNode.children.indexOf(el); this.setState({selectedIndex: index}); } render() { const index = this.state.selectedIndex; return ( <ul onClick={ this.handleClick.bind(this) }> <li className={ index === 0 && 'selected' }>Zero!</li> <li className={ index === 1 && 'selected' }>One!</li> <li className={ index === 2 && 'selected' }>Two!</li> </ul> ); } }
  13. Example: TabBar • This works fine until one day… •

    A new component is introduced to the site that also controls which tab is selected • Instead of pulling state management out of TabBar, make it a controllable
  14. The old(ish) way: The Controllable mixin A Brief Aside About

    Mixins
  15. –Sebastian Markbåge, who knows what idiomatic React is “…mixins (are)

    an escape hatch to work around reusability limitations… Idiomatic React reusable code should primarily be implemented in terms of composition and not inheritance.”
  16. –Dan Abramov, while explaining what Sebastian Markbåge meant “Mixins are

    dead. Long live composition.” –Sebastian Markbåge, again “Another alternative to mixins… Higher Order Components.”
  17. A function that takes an existing component and returns another

    component that wraps it. Higher-Order Component
  18. The new way: The Controllable Higher-Order Component! Example: TabBar

  19. class TabBar extends React.Component { handleClick(event) { if (!this.props.onSelectedIndexChange) return;

    const el = event.target; const index = el.parentNode.children.indexOf(el); this.props.onSelectedIndexChange(index); } render() { const index = this.props.selectedIndex; return ( <ul onClick={ this.handleClick.bind(this) }> <li className={ index === 0 && 'selected' }>Zero!</li> <li className={ index === 1 && 'selected' }>One!</li> <li className={ index === 2 && 'selected' }>Two!</li> </ul> ); } } TabBar.defaultProps = {selectedIndex: 0}; TabBar = controllable(TabBar, ['selectedIndex']);
  20. Always use props! • No state in TabBar* • the

    controllable HOC handles the magic * Technically, the newly decorated TabBar does have state. The controllable can be thought of as providing an optional binding to a per-component state store.
  21. Controllable TabBar <TabBar selectedIndex={1} /> Controlled: Uncontrolled: Uncontrolled (with a

    default value): <TabBar onSelectedIndexChange={handler} /> <TabBar defaultSelectedIndex={1} onSelectedIndexChange={handler} />
  22. The future way? The Controllable Decorator! Example: TabBar

  23. – Sebastian McKenzie in the Babel.js 5.0 release post “Yehuda

    Katz' stage 1 decorators proposal allows you to elegantly compose property descriptors and metadata decoration.”
  24. JavaScript Decorators! • An expression that evaluates to a function

    • Easily annotate and modify classes and properties • Can act like a factory, taking additional arguments at design time • Currently a Stage 1 TC39 (ECMAScript) Proposal • Supported in Babel.js 5.0! (Python style. Basically lifted straight from Python.)
  25. @controllable(['selectedIndex']) class TabBar extends React.Component { static defaultProps = {selectedIndex:

    0}; static propTypes = { selectedIndex: PropTypes.number.isRequired, onSelectedIndexChange: PropTypes.func, }; handleClick(event) { if (!this.props.onSelectedIndexChange) return; const el = event.target; const index = el.parentNode.children.indexOf(el); this.props.onSelectedIndexChange(index); } render() { const index = this.props.selectedIndex; return ( <ul onClick={ this.handleClick.bind(this) }> <li className={ index === 0 && 'selected' }>Zero!</li> <li className={ index === 1 && 'selected' }>One!</li> <li className={ index === 2 && 'selected' }>Two!</li> </ul> ); } }
  26. HZDG.COM LETTERTWO ERIC ELDREDGE LETTERTWO@GMAIL.COM HZDG PHOTO MISSING

  27. react-controllables https://github.com/matthewwithanm/react-controllables Another alternative to mixins…Higher Order https://twitter.com/sebmarkbage/status/565657028665016321 JavaScript Decorators

    Proposal https://github.com/wycats/javascript-decorators Mixins Are Dead. Long Live Composition https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order- Babel.js 5.0 Release Announcement http://babeljs.io/blog/2015/03/31/5.0.0/ Props vs State https://speakerdeck.com/lettertwo/props-vs-state