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

Higher Order Components in React at Voxxed Days Ticino 2016

Higher Order Components in React at Voxxed Days Ticino 2016

see code at https://gist.github.com/cef62/23b37d0fb8b47c0ed9c5a40a0ae15414

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

April 30, 2016
Tweet

More Decks by Matteo Ronchi

Other Decks in Programming

Transcript

  1. AJAX & DOM MANIPULATION <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>
  2. ANGULARJS <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>
  3. REACT <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>
  4. STATELESS FUNCTIONAL COMPONENTS 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>
  5. COMPONENT LIFECYCLE 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) {} }
  6. COMPONENT LIFECYCLE 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() {} }
  7. STATEFUL COMPONENTS 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> ) } }
  8. COMPONENTS SHARE FUNCTIONALITIES 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> } }
  9. MIXINS const LoadDataMixin = { loadData(url, targetField) { axios.get(url).then( ({

    data }) => this.setState({ [targetField]: data }) ) } }
  10. MIXINS 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> } })
  11. HOF 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 works!`)) // print -> It works!
  12. const Wrapped = () => <h1>Wrapped Comp</h1> const wrapper =

    (Comp) => () => { return <div><Comp /></div> } const FinalComponent = wrapper(Wrapped) const App = () => { return ( <div> <FinalComponent /> </div> ) }
  13. REDUCE DUPLICATION OF CODE 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>)
  14. DECORATOR PROPOSAL @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> } }
  15. - Declarative - Customizable - Easy to read - Enforce

    composition HOC VS MIXINS Imperative - Favors inheritance - Hard to read - Method names collision - Access component state -
  16. BEST PRACTICES HIGHER ORDER COMPONENTS • Expose wrapped component •

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