Slide 1

Slide 1 text

@coding_lawyer Unleash the power of the higher-order components David Kopal

Slide 2

Slide 2 text

@coding_lawyer 
 @coding_lawyer codinglawyer.net

Slide 3

Slide 3 text

@coding_lawyer lambdup.io

Slide 4

Slide 4 text

@coding_lawyer Who programs in React?

Slide 5

Slide 5 text

@coding_lawyer React HoCs Reusability Composition Recompose Introduction Conclusion

Slide 6

Slide 6 text

@coding_lawyer React HoCs Reusability Composition Recompose Introduction Conclusion

Slide 7

Slide 7 text

@coding_lawyer const SWChars = [ { name: 'Luke', side: 'light' }, { name: 'Darth Vader', side: 'dark' }, { name: 'Obi-wan Kenobi', side: 'light' }, { name: 'Palpatine', side: 'dark' } ] Render()

Slide 8

Slide 8 text

@coding_lawyer class FilteredList extends React.Component { constructor(props) { super(props) this.state = { side: 'dark' } } toggleState() { this.setState({ side: this.state.side === 'dark' ? 'light' : 'dark' }) } render() { const filtered = this.props.list.filter( char => char.side === this.state.side) return filtered.length > 1 ? (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} this.toggleState()}>Toggle
) : (
You need to have more than one character.
)}}

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

@coding_lawyer class FilteredList extends React.Component { constructor(props) { super(props) this.state = { side: 'dark' } } toggleState() { this.setState({ side: this.state.side === 'dark' ? 'light' : 'dark' }) } render() { const filtered = this.props.list.filter( char => char.side === this.state.side) return filtered.length > 1 ? (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} this.toggleState()}>Toggle
) : (
You need to have more than one character.
)}}

Slide 11

Slide 11 text

@coding_lawyer FilteredList (logic + presentation) not reusable

Slide 12

Slide 12 text

@coding_lawyer class FilteredList extends React.Component { constructor(props) { super(props) this.state = { side: 'dark' } } toggleState() { this.setState({ side: this.state.side === 'dark' ? 'light' : 'dark' }) } render() { const filtered = this.props.list.filter( char => char.side === this.state.side) return filtered.length > 1 ? (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} this.toggleState()}>Toggle
) : (
You need to have more than one character.
)}}

Slide 13

Slide 13 text

@coding_lawyer React HoCs Reusability Composition Recompose Introduction Conclusion

Slide 14

Slide 14 text

@coding_lawyer smart / presentational component

Slide 15

Slide 15 text

@coding_lawyer const SWChars = [ { name: 'Luke', side: 'light' }, { name: 'Darth Vader', side: 'dark' }, { name: 'Obi-wan Kenobi', side: 'light' }, { name: 'Palpatine', side: 'dark' } ] const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) Render()

Slide 16

Slide 16 text

@coding_lawyer const SWChars = [ { name: 'Luke', side: 'light' }, { name: 'Darth Vader', side: 'dark' }, { name: 'Obi-wan Kenobi', side: 'light' }, { name: 'Palpatine', side: 'dark' } ] const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) Render() reusable

Slide 17

Slide 17 text

@coding_lawyer DisplayList (presentation) reusable

Slide 18

Slide 18 text

@coding_lawyer const FilteredList = ({ list, side }) => { const filteredList = list.filter(char => char.side === side) return filteredList.map(char =>
{/* render UI */}
) } Render()

Slide 19

Slide 19 text

@coding_lawyer const FilteredList = ({ list, side }) => { const filteredList = list.filter(char => char.side === side) return filteredList.map(char =>
{/* render UI */}
) } Render() not reusable

Slide 20

Slide 20 text

@coding_lawyer FilteredList (logic + presentation) not reusable

Slide 21

Slide 21 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side }) => { const transformedProps = list.filter(char => char.side === side) return } const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) const FilteredList = withFilterProps(DisplayList) Render()

Slide 22

Slide 22 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side })=> { const transformedProps = list.filter(char => char.side === side) return } const FilteredList = withFilterProps(DisplayList)

Slide 23

Slide 23 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side })=> { const transformedProps = list.filter(char => char.side === side) return } const FilteredList = withFilterProps(DisplayList) Higher-order component

Slide 24

Slide 24 text

@coding_lawyer const HoC = BaseComponent => EnhancedComponent

Slide 25

Slide 25 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side })=> { const transformedProps = list.filter(char => char.side === side) return } const FilteredList = withFilterProps(DisplayList) Higher-order component

Slide 26

Slide 26 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side })=> { const transformedProps = list.filter(char => char.side === side) return } const FilteredList = withFilterProps(DisplayList) Higher-order component

Slide 27

Slide 27 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side }) => { const transformedProps = list.filter(char => char.side === side) return } const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) const FilteredList = withFilterProps(DisplayList) reusable

Slide 28

Slide 28 text

@coding_lawyer DisplayList (presentation) withFilterProps (logic) reusable

Slide 29

Slide 29 text

@coding_lawyer class FilteredList extends React.Component { constructor(props) { super(props) this.state = { side: 'dark' } } toggleState() { this.setState({ side: this.state.side === 'dark' ? 'light' : 'dark' }) } render() { const filtered = this.props.list.filter( char => char.side === this.state.side) return filtered.length > 1 ? (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} this.toggleState()}>Toggle
) : (
You need to have more than one character.
)}}

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

@coding_lawyer class FilteredList extends React.Component { constructor(props) { super(props) this.state = { side: 'dark' } } toggleState() { this.setState({ side: this.state.side === 'dark' ? 'light' : 'dark' }) } render() { const filtered = this.props.list.filter( char => char.side === this.state.side) return filtered.length > 1 ? (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} this.toggleState()}>Toggle
) : (
You need to have more than one character.
)}}

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

@coding_lawyer React HoCs Reusability Composition Recompose Introduction Conclusion

Slide 35

Slide 35 text

@coding_lawyer decoupling component from the data

Slide 36

Slide 36 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side }) => { const transformedProps = list.filter(char => char.side === side) return } const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) const FilteredList = withFilterProps(DisplayList) reusable

Slide 37

Slide 37 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side }) => { const transformedProps = list.filter(char => char.side === side) return }

Slide 38

Slide 38 text

@coding_lawyer const withFilterProps = BaseComponent => ({ list, side }) => { const transformedProps = list.filter(char => char.side === side) return } not reusable

Slide 39

Slide 39 text

@coding_lawyer DisplayList (presentation) withFilterProps (logic) not reusable reusable

Slide 40

Slide 40 text

@coding_lawyer not reusable const withFilterProps = BaseComponent => ({ list, side }) => { const transformedProps = list.filter(char => char.side === side) return } reusable const withTransformProps = transformFunc => BaseComponent => baseProps => { const transformedProps = transformFunc(baseProps) return }

Slide 41

Slide 41 text

@coding_lawyer const withTransformProps = transformFunc => BaseComponent => baseProps => { const transformedProps = transformFunc(baseProps) return } const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) const FilteredList = withTransformProps( ({ list, side }) => ({ list: list.filter(char => char.side === side) }))(DisplayList) Render()

Slide 42

Slide 42 text

@coding_lawyer const withTransformProps = transformFunc => BaseComponent => baseProps => { const transformedProps = transformFunc(baseProps) return } const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) const FilteredList = withTransformProps( ({ list, side }) => ({ list: list.filter(char => char.side === side) }))(DisplayList) Render()

Slide 43

Slide 43 text

@coding_lawyer const AddedList = withTransformProps( ({ list, side }) => ({ list: [ …list, { name: 'Han Solo', side: 'light' } ] }))(DisplayList)

Slide 44

Slide 44 text

@coding_lawyer const withTransformProps = transformFunc => BaseComponent => baseProps => { const transformedProps = transformFunc(baseProps) return } const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) const FilteredList = withTransformProps( ({ list, side }) => ({ list: list.filter(char => char.side === side) }))(DisplayList) Render()

Slide 45

Slide 45 text

@coding_lawyer const HoC = config => BaseComponent => EnhancedComponent

Slide 46

Slide 46 text

@coding_lawyer avoid unnecessary duplication

Slide 47

Slide 47 text

@coding_lawyer DisplayList (presentation) withTransformProps (logic) reusable reusable

Slide 48

Slide 48 text

@coding_lawyer import { connect } from 'react-redux' const mapStateToProps = state => ({ /* piece of global state */ }) const mapDispatchToProps = dispatch => ({ /* global state handlers*/ }) export default connect( mapStateToProps, mapDispatchToProps )(Component)

Slide 49

Slide 49 text

@coding_lawyer import { connect } from 'react-redux' const mapStateToProps = state => ({ /* piece of global state */ }) const mapDispatchToProps = dispatch => ({ /* global state handlers*/ }) export default connect( mapStateToProps, mapDispatchToProps )(Component)

Slide 50

Slide 50 text

@coding_lawyer import { connect } from 'react-redux' const mapStateToProps = state => ({ /* piece of global state */ }) const mapDispatchToProps = dispatch => ({ /* global state handlers*/ }) export default connect( mapStateToProps, mapDispatchToProps )(Component)

Slide 51

Slide 51 text

@coding_lawyer import { connect } from 'react-redux' const mapStateToProps = state => ({ /* piece of global state */ }) const mapDispatchToProps = dispatch => ({ /* global state handlers*/ }) export default connect( mapStateToProps, mapDispatchToProps )(Component)

Slide 52

Slide 52 text

@coding_lawyer const withCondition = (conditionFn, EitherComponent) => BaseComponent => baseProps => conditionFn(baseProps) ? ( ) : ( )

Slide 53

Slide 53 text

@coding_lawyer React HoCs Reusability Composition Recompose Introduction Conclusion

Slide 54

Slide 54 text

@coding_lawyer const number = 15 const increment = num => num + 5 const decrement = num => num - 3 const multiply = num => num * 2 const operation = increment(decrement(multiply(number))) console.log(operation) //32

Slide 55

Slide 55 text

@coding_lawyer const number = 15 const increment = num => num + 5 const decrement = num => num - 3 const multiply = num => num * 2 const operation = increment(decrement(multiply(number))) console.log(operation) //32

Slide 56

Slide 56 text

@coding_lawyer const number = 15 const increment = num => num + 5 const decrement = num => num - 3 const multiply = num => num * 2 const operation = increment(decrement(multiply(number))) console.log(operation) //32

Slide 57

Slide 57 text

@coding_lawyer const withTransformProps = transformFunc => BaseComponent => baseProps => { const transformedProps = transformFunc(baseProps) return } const withCondition = (conditionFn, EitherComponent) => BaseComponent => baseProps => conditionFn(baseProps) ? ( ) : ( ) const ConditionedList = withCondition(({ list }) => list.length <= 1, Warning)(DisplayList) const FilteredList = withTransformProps(({ list, side }) => ({ list: list.filter(char => char.side === side) }))(ConditionedList)

Slide 58

Slide 58 text

@coding_lawyer const DisplayList = ({ list }) => list.map(char =>
{/* render UI */}
) const Warning = () =>
You need to have more than one character
Render()

Slide 59

Slide 59 text

@coding_lawyer HoCs talk to each other via props

Slide 60

Slide 60 text

@coding_lawyer const ConditionedList = withCondition(({ list }) => list.length <= 1, Warning)(DisplayList) const FilteredList = withTransformProps(({ list, side }) => ({ list: list.filter(char => char.side === side) }))(ConditionedList)

Slide 61

Slide 61 text

@coding_lawyer const FilteredList = withTransformProps(({ list, side }) => ({ list: list.filter(char => char.side === side) }))(withCondition(({ list }) => list.length <= 1, Warning)(DisplayList))

Slide 62

Slide 62 text

@coding_lawyer const compose = (...hocs) => BaseComponent => hocs.reduceRight((acc, hoc) => hoc(acc), BaseComponent) const enhance = compose( withTransformProps(({ list, side }) => ({ list: list.filter(char => char.side === side) })), withCondition(({ list }) => list.length <= 1, Warning) ) const FilteredList = enhance(DisplayList)

Slide 63

Slide 63 text

@coding_lawyer const withTransformProps = transformFunc => BaseComponent => baseProps => { const transformedProps = transformFunc(baseProps) return } const withCondition = (conditionFn, EitherComponent) => BaseComponent => baseProps => conditionFn(baseProps) ? ( ) : ( ) const enhance = compose( withTransformProps(({ list, side }) => ({ list: list.filter(char => char.side === side) })), withCondition(({ list }) => list.length <= 1, Warning) ) const FilteredList = enhance(DisplayList)

Slide 64

Slide 64 text

@coding_lawyer withTransformProps (logic) reusable reusable withCondition (logic) DisplayList (presentation) reusable

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

@coding_lawyer React HoCs Reusability Composition Recompose Introduction Conclusion

Slide 67

Slide 67 text

@coding_lawyer import { withProps, branch, compose } from 'recompose' const withWarning = () => () =>
You need to have more than one character
const enhance = compose( withProps(({ list, side }) => ({ list: list.filter(char => char.side === side) })), branch(({ list }) => list.length <= 1, withWarning) ) const FilteredList = enhance(DisplayList)

Slide 68

Slide 68 text

@coding_lawyer import { withProps, branch, compose, withState } from ‘recompose' const withWarning = () => () => (
You need to have more than one character
) const enhance = compose( withState('state', 'setState', 'dark'), withProps(({ list, state }) => ({ filtered: list.filter(char => char.side === state) })), branch(({ filtered }) => filtered.length <= 1, withWarning) )

Slide 69

Slide 69 text

@coding_lawyer const DisplayList = ({ filtered, setState, state }) => (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} setState(state === 'dark' ? 'light' : 'dark')}> Toggle
) const FilteredList = enhance(DisplayList) Render()

Slide 70

Slide 70 text

@coding_lawyer const DisplayList = ({ filtered, setState, state }) => (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} setState(state === 'dark' ? 'light' : 'dark')}> Toggle
) const FilteredList = enhance(DisplayList) Render()

Slide 71

Slide 71 text

@coding_lawyer const withWarning = () => () => (
You need to have more than one character
) const enhance = compose( withState('state', 'setState', 'dark'), withProps(({ list, state }) => ({ filtered: list.filter(char => char.side === state) })), branch(({ filtered }) => filtered.length <= 1, withWarning), withHandlers({ toggleState: ({ state, setState }) => () => setState(state === 'dark' ? 'light' : 'dark') }) )

Slide 72

Slide 72 text

@coding_lawyer const withWarning = () => () => (
You need to have more than one character
) const enhance = compose( withState('state', 'setState', 'dark'), withProps(({ list, state }) => ({ filtered: list.filter(char => char.side === state) })), branch(({ filtered }) => filtered.length <= 1, withWarning), withHandlers({ toggleState: ({ state, setState }) => () => setState(state === 'dark' ? 'light' : 'dark') }) )

Slide 73

Slide 73 text

@coding_lawyer const DisplayList = ({ filtered, toggleState }) => (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} Toggle
) const FilteredList = enhance(DisplayList) Render()

Slide 74

Slide 74 text

@coding_lawyer const withWarning = () => () => (
You need to have more than one character
) const enhance = compose( withState('state', 'setState', 'dark'), withProps(({ list, state }) => ({ filtered: list.filter(char => char.side === state) })), branch(({ filtered }) => filtered.length <= 1, withWarning), withHandlers({ toggleState: ({ state, setState }) => () => setState(state === 'dark' ? 'light' : 'dark') }) )

Slide 75

Slide 75 text

@coding_lawyer Recompose makes HoCs’ composition easier

Slide 76

Slide 76 text

@coding_lawyer withState (logic) reusable reusable reusable withProps (logic) branch (logic) withHandlers (logic) DisplayList (presentation) reusable reusable

Slide 77

Slide 77 text

@coding_lawyer React HoCs Reusability Composition Recompose Introduction Conclusion

Slide 78

Slide 78 text

@coding_lawyer class FilteredList extends React.Component { constructor(props) { super(props) this.state = { side: 'dark' } } toggleState() { this.setState({ side: this.state.side === 'dark' ? 'light' : 'dark' }) } render() { const filtered = this.props.list.filter( char => char.side === this.state.side) return filtered.length > 1 ? (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} this.toggleState()}>Toggle
) : (
You need to have more than one character.
)}}

Slide 79

Slide 79 text

@coding_lawyer const DisplayList = ({ filtered, toggleState }) => (
{filtered.map(char => (
Character: {char.name}
Side: {char.side}
))} Toggle
) const FilteredList = enhance(DisplayList)

Slide 80

Slide 80 text

@coding_lawyer const withWarning = () => () => (
You need to have more than one character
) const enhance = compose( withState('state', 'setState', 'dark'), withProps(({ list, state }) => ({ filtered: list.filter(char => char.side === state) })), branch(({ filtered }) => filtered.length <= 1, withWarning), withHandlers({ toggleState: ({ state, setState }) => () => setState(state === 'dark' ? 'light' : 'dark') }) )

Slide 81

Slide 81 text

@coding_lawyer presentational components that can be enhanced by HoC(s)

Slide 82

Slide 82 text

@coding_lawyer data fetching local / global state local storage / cookies

Slide 83

Slide 83 text

@coding_lawyer const withWarning = () => () => (
You need to have more than one character
) const enhance = compose( withState('state', 'setState', 'dark'), withProps(({ list, state }) => ({ filtered: list.filter(char => char.side === state) })), branch(({ filtered }) => filtered.length <= 1, withWarning), withHandlers({ toggleState: ({ state, setState }) => () => setState(state === 'dark' ? 'light' : 'dark') }) )

Slide 84

Slide 84 text

@coding_lawyer const withWarning = () => () => (
You need to have more than one character
) const enhance = compose( withState('state', 'setState', 'dark'), withProps(({ list, state }) => ({ filtered: list.filter(char => char.side === state) })), branch(({ filtered }) => filtered.length <= 1, withWarning), withHandlers({ toggleState: ({ state, setState }) => () => setState(state === 'dark' ? 'light' : 'dark') }) )

Slide 85

Slide 85 text

@coding_lawyer 
 @coding_lawyer codinglawyer.net

Slide 86

Slide 86 text

@coding_lawyer Questions?

Slide 87

Slide 87 text

@coding_lawyer Thank You

Slide 88

Slide 88 text

@coding_lawyer @coding_lawyer codinglawyer.net github.com/codinglawyer/hocs-code