higher-order components — ilya gelman

52
HIGHER-ORDER COMPONENTS Ilya Gelman

Upload: 500tech

Post on 21-Jan-2017

101 views

Category:

Technology


0 download

TRANSCRIPT

HIGHER-ORDER COMPONENTS

Ilya Gelman

Ilya Gelman

- Organizer of AngularJS-IL & ReactJS-Israel - Organizer of ReactNext 2016 - Author of The Complete Redux Book

Consultant @ 500Tech

JS

Functions are first-class citizens

wut?

function square(number) { return number * number; } var square = function (number) { return number * number;};

console.log(square);const square = (number) => number * number;array.map(square);

function square(number) { return number * number;} var square = function (number) { return number * number; };

console.log(square);const square = (number) => number * number;array.map(square);

function square(number) { return number * number;} var square = function (number) { return number * number; };

console.log(square); const square = (number) => number * number;array.map(square);

function square(number) { return number * number;} var square = function (number) { return number * number;};

console.log(square);const square = (number) => number * number; array.map(square);

HIGHER-ORDER

HIGHER-ORDER

const a = () => { ... }const b = (f) => { ... }b(a);

const c = () => { return () => { ... }}

() => () => () => () => () => () =>

const awesomeMiddleware = ({ dispatch, getState }) => (next) => (action) => { ... }

const awesomeMiddleware = ({ dispatch, getState }) => (next) => (action) => { ... }

HIGHER-ORDER COMPONENT

class BaseComponent extends React.Component { ... }class InputComponent extends BaseComponent { ... }class Checkbox extends InputComponent { ... }

COMPOSITION VS.

INHERITANCE

COMPOSITION VS. INHERITANCE

What things do What things are

Extend as you need Predict the future

Separated Tightly coupled

Startup Enterprise

THE BANANA/GORILLA PROBLEM

class Checkbox extends React.Component { constructor() { Object.asign(this, { validate(...), enlargeOnMobile(), disableIf(...) }) }

}

STATELESS COMPONENTS

Available from React 0.14+

const DropDown = ({ list = [] }) => ( <div className="dropdown"> <ul> { list.map((item) => <li>{ item }</li>) } </ul> </div>);

const DropDown = ({ list = [] }) => ( <div className="dropdown"> <ul> { list.map((item) => <li>{ item }</li>) } </ul> </div>);

const loadOptions = (Component) => class LoadOptions extends React.Component { constructor() { super(); this.state = { loaded: false, list: null } } componentDidMount() { API.fetchOptions().then( (list) => this.setState({ loaded: true, list }) ); } render() { return this.state.loaded && <Component list={ this.state.list }/>; } };

const loadOptions = (Component) => class LoadOptions extends React.Component { constructor() { super(); this.state = { loaded: false, list: null } } componentDidMount() { API.fetchOptions().then( (list) => this.setState({ loaded: true, list }) ); } render() { return this.state.loaded && <Component list={ this.state.list }/>; } };

const loadOptions = (Component) => class LoadOptions extends React.Component { constructor() { super(); this.state = { loaded: false, list: null } } componentDidMount() { API.fetchOptions().then( (list) => this.setState({ loaded: true, list }) ); } render() { return this.state.loaded && <Component list={ this.state.list }/>; } };

const loadOptions = (Component) => class LoadOptions extends React.Component { constructor() { super(); this.state = { loaded: false, list: null } } componentDidMount() { API.fetchOptions().then( (list) => this.setState({ loaded: true, list }) ); } render() { return this.state.loaded && <Component list={ this.state.list }/>; } };

const loadOptions = (Component) => class LoadOptions extends React.Component { constructor() { super(); this.state = { loaded: false, list: null } } componentDidMount() { API.fetchOptions().then( (list) => this.setState({ loaded: true, list }) ); } render() { return this.state.loaded && <Component list={ this.state.list }/>; } };

const loadOptions = (Component) => class LoadOptions ...

const loadOptions = (Component) => { class LoadOptions ...

return React.createElement(LoadOptions); }

const DropDown = (...) => (...); const LazyDropDown = loadOptions(DropDown); const App = () => ( <div> <LazyDropDown /> </div>);

const DropDown = (...) => (...);

const App = () => ( <div> { loadOptions(DropDown) } </div>);

const logProps = (Component) => { console.log(Component.props); return Component; };

const DropDown = (...) => (...);

const App = () => ( <div> { logProps(<DropDown list={ … } />) } </div>);

DECORATOR

npm i recompose -S

react-next.com

import { withState } from 'recompose'; const DropDown = ({ list, visible, setVisibility }) => ( <div className="dropdown" onClick={ () => setVisibility(visible => !visible) }> { visible && <ul>/* { ... } */</ul> } </div>); export default withState( 'visible', // value on state 'setVisibility', // callback to change the state false // initial value)(DropDown);

import { withState } from 'recompose'; const DropDown = ({ list, visible, setVisibility }) => ( <div className="dropdown" onClick={ () => setVisibility(visible => !visible) }> { visible && <ul>/* { ... } */</ul> } </div>); export default withState( 'visible', // value on state 'setVisibility', // callback to change the state false // initial value)(DropDown);

import { withState } from 'recompose';const DropDown = ({ list, visible, setVisibility }) => ( <div className="dropdown" onClick={ () => setVisibility(visible => !visible) }> { visible && <ul>/* { ... } */</ul> } </div>); export default withState( 'visible', // value on state 'setVisibility', // callback to change the state false // initial value)(DropDown);

import { withState } from 'recompose';const DropDown = ({ list, visible, setVisibility }) => ( <div className="dropdown" onClick={ () => setVisibility(visible => !visible) }> { visible && <ul>/* { ... } */</ul> } </div>); export default withState( 'visible', // value on state 'setVisibility', // callback to change the state false // initial value)(DropDown);

import { withState, mapProps, pure } from 'recompose';

...

withState(..., mapProps(..., pure(MyComponent)));

pick(unique(flatten(map(array))));

))))))));

pick(unique(flatten(map(array)))); compose( pick, unique, flatten, map)(array);

function compose(...fns) { const lastFn = fns[fns.length - 1] const restFns = fns.slice(0, -1)

return (target) => restFns.reduceRight( (composed, f) => f(composed), lastFn(target) )

}

function compose(...fns) { const lastFn = fns[fns.length - 1] const restFns = fns.slice(0, -1)

return (target) => restFns.reduceRight( (composed, f) => f(composed), lastFn(target) )

}

function compose(...fns) { const lastFn = fns[fns.length - 1] const restFns = fns.slice(0, -1)

return (target) => restFns.reduceRight( (composed, f) => f(composed), lastFn(target) )

}

function compose(...fns) { const lastFn = fns[fns.length - 1] const restFns = fns.slice(0, -1)

return (target) => restFns.reduceRight( (composed, f) => f(composed), lastFn(target) )

}

– Prefer composition over inheritance

– Use stateless components

– npm install recompose

– Don’t miss ReactNext 2016

Read our blog:http://blog.500tech.com

Ilya [email protected]