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

Controllable React Components

Controllable React Components

Slides from a presentation about react-controllables.

Originally given at ReactDC on 4/7/2015

Eric Eldredge

April 07, 2015
Tweet

More Decks by Eric Eldredge

Other Decks in Programming

Transcript

  1. …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
  2. 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} />
  3. 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
  4. 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
  5. 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> ); } }
  6. 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
  7. –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.”
  8. –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.”
  9. A function that takes an existing component and returns another

    component that wraps it. Higher-Order Component
  10. 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']);
  11. 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.
  12. Controllable TabBar <TabBar selectedIndex={1} /> Controlled: Uncontrolled: Uncontrolled (with a

    default value): <TabBar onSelectedIndexChange={handler} /> <TabBar defaultSelectedIndex={1} onSelectedIndexChange={handler} />
  13. – 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.”
  14. 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.)
  15. @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> ); } }
  16. 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