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

Higher Order Components in React at italian jsD...

Higher Order Components in React at italian jsDay 2016

React is “a Javascript library for building user interfaces”. The core of React follows many concepts of Functional Programming, code composition is one of its main features. In React there are 2 main types of building blocks: 'stateful' components and 'stateless' components. The former type is defined by elements owning the application logic; this kind of components often is bound to external elements and owns a state. The latter type is composed of 'pure' components where all dynamic data is received from their props and no internal state is defined. This approach often produces replication of functionalities in several components; 'Higher Order Components' (HoC) is the natural solution for this scenario and is a more functional alternative to traditional React 'mixins'. An HoC abstracts component functionalities and offers an easy way to compose them when required making the code more maintainable and avoiding duplicates. During the session we will see how to implements this pattern taking attention on its pros and cons.

Matteo Ronchi

May 12, 2016
Tweet

More Decks by Matteo Ronchi

Other Decks in Programming

Transcript

  1. <div> <h1 id=“my-title”></h1> <p id=“my-contents”></p> </div> <script type=“text/javascript”> $(document).ready(() =>

    { $.ajax(‘my-url.php’) .done((data) => { $(‘#my-title’).text(data.title) $(‘#my-contents’).text(data.contents) }) }) </script> jQuery
  2. <div ng-app=“app”> <my-page my-url=“my-url.php”></my-page> </div> <script type=“text/javascript”> angular.module(‘app’, []).directive(‘myPage’, function($http)

    { return { scope: { myUrl: ‘@’} template: `<div> <h1>{{myTitle}}</h1> <p>{{myContents}}</p> </div>` link: function(scope) { $http.get(scope.myUrl).success((data) => { scope.myTitle = data.title scope.myContents = data.contents }) } } }) </script> AngularJS
  3. <div id=“root” /> <script type=“text/javascript”> class App extends React.Component {

    componentWillMount() { axios.get(this.props.myUrl).then(({ title, contents }) => { this.setState({ title, contents }) }) } render() { return ( <div> <h1>{this.state.title}</h1> <p>{this.state.contents}</p> </div> ) } } ReactDOM.render(<App myUrl=“my-url.php” />, document.getElementById(‘root’)) </script> React
  4. const Label = (props) => <h1>{props.children}</h1> const Icon = (props)

    => { return ( <i className="fa fa-anchor"> {props.label} </i> ) } const App = () => <div> <Label>Some label</Label> <Icon label="ready to sail" /> </div> Stateless Functional Components
  5. class MyComponent extends React.Component { // Component Initialization constructor(props) {}

    // component ready to be mounted componentWillMount() {} // Props have changed componentWillReceiveProps(nextProps) {} // prevent rendering if not required shouldComponentUpdate(nextProps, nextState) {} // Prepare before rendering componentWillUpdate(nextProps, nextState) {} } Component Lifecycle
  6. class MyComponent extends React.Component { // create/update the component render()

    {} // Component DOM it’s been created componentDidMount() {} // Component DOM it’s been updated componentDidUpdate(prevProps, prevState) {} // cleanup before component destruction componentWillUnmount() {} } Component Lifecycle
  7. class Toggle extends React.Component { constructor(props) { super(props) this.state =

    { enabled: false } this.toggle = this.toggle.bind(this) } toggle() { this.setState({ enabled: !this.state.enabled }) } render() { const cls = this.state.enabled ?'btn-success':'btn-danger' return ( <button className={‘btn ‘ + cls} onClick={this.toggle}> {this.props.label} </button> ) } } Stateful Components
  8. class Box extends React.Component { constructor(props) { this.state = {

    info: {} } } componentWillMount() { axios.get(this.props.url).then( ({ data }) => this.setState({ info: data }) ) } render() { return <div>{this.state.info.text}</div> } } class User extends React.Component { constructor(props) { this.state = { user: {} } } componentWillMount() { axios.get(this.props.url).then( ({ data }) => this.setState({ user: data }) ) } render() { return <div>{this.state.user.name}</div> } } Components Share Functionalities
  9. const LoadDataMixin = { loadData(url, targetField) { axios.get(url).then( ({ data

    }) => this.setState({ [targetField]: data }) ) } } React Mixins
  10. const BoxInfo = React.createClass({ mixins: [LoadDataMixin], getInitialState() { return {

    info: {} } }, componentWillMount() { this.loadData(this.props.url, 'info') }, render() { return <div>{this.state.info.text}</div> } }) const UserPanel = React.createClass({ mixins: [LoadDataMixin], getInitialState() { return { user: {} } }, componentWillMount() { this.loadData(this.props.url, 'user') }, render() { return <div>{this.state.user.name}</div> } }) React Mixins
  11. function applyVat(vat) { return (value) => ((vat + 100) /

    100) * value } const applyVat22 = applyVat(22) console.log(applyVat22(100)) // print -> 122 function maybe(condition, action) { if (condition) { action() } } const action = (msg) => () => console.log(msg) maybe(true, action(`It works!`)) maybe(false, action(`Doesn't work!`)) // print -> It works! HoF
  12. const Wrapped = () => <h1>Wrapped Comp</h1> const wrapper =

    (Comp) => () => { return <div><Comp /></div> } const FinalComponent = wrapper(Wrapped) const App = () => { return ( <div> <FinalComponent /> </div> ) } Simple HoC
  13. const loadData = (Component) => { return class LoadDataWrapper extends

    React.Component { constructor(props) { this.state = { data: {} } } componentWillMount() { axios.get(this.props.url).then( ({ data }) => this.setState({ data }) ) } render() { const props = Object.assign({}, this.props, this.state) return React.createElement(Component, props) } } } const Box = loadData((props) => <div>{props.data.text}</div>) const User = loadData((props) => <div>{props.data.name}</div>) Reduce Duplication of Code
  14. @loadData class Box extends React.Component { render() { return <div>{this.props.data.text}</div>

    } } @loadData class User extends React.Component { render() { return <div>{this.props.data.name}</div> } } // See https://github.com/wycats/javascript-decorators Decorator Proposal
  15. HoC vs Mixins - Declarative - Customizable - Easy to

    read - Enforce composition Imperative - Favors inheritance - Hard to read - Method names collision - Access component state -
  16. HoC - Best Practices • Expose wrapped component • Pass

    props to wrapped component • Expose statics methods and props from wrapped component