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

React HOC and React Hooks

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for React Moscow React Moscow
December 11, 2019

React HOC and React Hooks

Dmitry Grigorov, Gazprombank @ React Moscow Meetup 5
Dec 11, 2019, Gazprombank

Вы когда-нибудь сталкивались с дублированием кода в компонентах? Пытались ли вы сделать так, чтобы компонент был комбинируемый и был максимально переиспользуемым другими компонентами? В своем докладе Дмитрий расскажет о некоторых приемах использования компонентов высшего порядка (High Order Components) и сравним их с приемами, которые принесли хуки в "синтаксис" Реакта. Все думают, что одно пришло заменить другое, но на данном докладе мы попытаемся разобраться – действительно ли это так?

Avatar for React Moscow

React Moscow

December 11, 2019
Tweet

More Decks by React Moscow

Other Decks in Programming

Transcript

  1. Как называется компонент, у которого есть cтейт? class StatefullComponent extends

    Component { state = { date: new Date() } componentDidMount() { ... } componentWillUnmount() { ... } tick() { ... } render() { return ( <div> ... </div> ); } }
  2. const hoc = (overrideProps) => { return (BaseComponent) => {

    return (props) => { return <BaseComponent {...props} {...overrideProps} /> }; } } Stateless Component 9
  3. const hoc = (overrideProps) => { return (BaseComponent) => {

    return class extends React.Component { shouldComponentUpdate() { return false; } render() { return ( <BaseComponent {...this.props} {...overrideProps} /> ); } } } } Stateful Component 10
  4. class Status extends Component { state = { isToggle: false

    }; toggle = () => { const { isToggle } = this.state; this.setState({ isToggle: !isToggle }) }; render() { const { isToggle } = this.state; const { status } = this.props; return ( <span onClick={this.toggle} > { status } { isToggle && <StatusList /> } </span> ); }; } 21 class Tooltip extends Component { state = { isToggle: false }; show = () => { this.setState({ isToggle: true }) }; hide = () => { this.setState({ isToggle: false }) }; render() { const { isToggle } = this.state; const { children, text } = this.props; return ( <span onMouseEnter={this.show} onMouseLeave={this.hide}> { isToggle && <div className="Tooltip">{ text }</div>} <span>{ children }</span> </span> ); }; }
  5. class Tooltip extends Component { state = { isToggle: false

    }; show = () => { this.setState({ isToggle: true }) }; hide = () => { this.setState({ isToggle: false }) }; render() { const { isToggle } = this.state; const { children, text } = this.props; return ( <span onMouseEnter={this.show} onMouseLeave={this.hide}> { isToggle && <div className="Tooltip">{ text }</div>} <span>{ children }</span> </span> ); }; } class Status extends Component { state = { isToggle: false }; toggle = () => { const { isToggle } = this.state; this.setState({ isToggle: !isToggle }) }; render() { const { isToggle } = this.state; const { status } = this.props; return ( <span onClick={this.toggle} > { status } { isToggle && <StatusList /> } </span> ); }; } 22
  6. class Status extends Component { state = { isToggle: false

    }; toggle = () => { const { isToggle } = this.state; this.setState({ isToggle: !isToggle }) }; render() { const { isToggle } = this.state; const { status } = this.props; return ( <span onClick={this.toggle} > { status } { isToggle && <StatusList /> } </span> ); }; } 23 class Tooltip extends Component { state = { isToggle: false }; show = () => { this.setState({ isToggle: true }) }; hide = () => { this.setState({ isToggle: false }) }; render() { const { isToggle } = this.state; const { children, text } = this.props; return ( <span onMouseEnter={this.show} onMouseLeave={this.hide}> { isToggle && <div className="Tooltip">{ text }</div>} <span>{ children }</span> </span> ); }; }
  7. class Status extends Component { state = { isToggle: false

    }; toggle = () => { const { isToggle } = this.state; this.setState({ isToggle: !isToggle }) }; render() { const { isToggle } = this.state; const { status } = this.props; return ( <span onClick={this.toggle} > { status } { isToggle && <StatusList /> } </span> ); }; } 24 class Tooltip extends Component { state = { isToggle: false }; show = () => { this.setState({ isToggle: true }) }; hide = () => { this.setState({ isToggle: false }) }; render() { const { isToggle } = this.state; const { children, text } = this.props; return ( <span onMouseEnter={this.show} onMouseLeave={this.hide}> { isToggle && <div className="Tooltip">{ text }</div>} <span>{ children }</span> </span> ); }; }
  8. const Tooltip = withState('isToggle', 'toggle', false)( ({ text, children, isToggle,

    toggle }) => <span onMouseEnter={ () => toggle(true) } onMouseLeave={ () => toggle(false) } > { isToggle && ( <div className="Tooltip">{ text }</div> )} <span>{ children }</span> </span> ); 26
  9. const Tooltip = withState('isToggle', 'toggle', false)( ({ text, children, isToggle,

    toggle }) => <span onMouseEnter={ () => toggle(true) } onMouseLeave={ () => toggle(false) } > { isToggle && ( <div className="Tooltip">{ text }</div> )} <span>{ children }</span> </span> ); 27
  10. const Tooltip = withState('isToggle', 'toggle', false)( ({ text, children, isToggle,

    toggle }) => <span onMouseEnter={ () => toggle(true) } onMouseLeave={ () => toggle(false) } > { isToggle && ( <div className="Tooltip">{ text }</div> )} <span>{ children }</span> </span> ); 28
  11. const Tooltip = withState('isToggle', 'toggle', false)( ({ text, children, isToggle,

    toggle }) => <span onMouseEnter={ () => toggle(true) } onMouseLeave={ () => toggle(false) } > { isToggle && ( <div className="Tooltip">{ text }</div> )} <span>{ children }</span> </span> ); 29
  12. const withToggle = compose( withState('toggledOn', 'toggle', false), withHandlers({ show: ({

    toggle }) => (e) => toggle(true), hide: ({ toggle }) => (e) => toggle(false), toggle: ({ toggle }) => (e) => toggle(x => !x) }) ) 31 31
  13. const withToggle = compose( withState('toggledOn', 'toggle', false), withHandlers({ show: ({

    toggle }) => (e) => toggle(true), hide: ({ toggle }) => (e) => toggle(false), toggle: ({ toggle }) => (e) => toggle(x => !x) }) ) 32 32 withToggle(Component)
  14. class Status extends Component { state = { isToggle: false

    }; toggle = () => { const { isToggle } = this.state; this.setState({ isToggle: !isToggle }) }; render() { const { isToggle } = this.state; const { status } = this.props; return ( <span onClick={this.toggle} > { status } { isToggle && <StatusList /> } </span> ); }; } 33 class Tooltip extends Component { state = { isToggle: false }; show = () => { this.setState({ isToggle: true }) }; hide = () => { this.setState({ isToggle: false }) }; render() { const { isToggle } = this.state; const { children, text } = this.props; return ( <span onMouseEnter={this.show} onMouseLeave={this.hide}> { isToggle && <div className="Tooltip">{ text }</div>} <span>{ children }</span> </span> ); }; } Before
  15. const withToggle = compose( withState('toggledOn', 'toggle', false), withHandlers({ show: ({

    toggle }) => (e) => toggle(true), hide: ({ toggle }) => (e) => toggle(false), toggle: ({ toggle }) => (e) => toggle((current) => !current) }) ) const Status = withToggle(({ status, toggledOn, toggle }) => <span onClick={ toggle }> { status } { toggledOn && <StatusList /> } </span> ); const Tooltip = withToggle(({ text, children, toggledOn, show, hide }) => <span> { toggledOn && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ show } onMouseLeave={ hide }>{ children }</span> </span> ); 34 34 After
  16. Плюсы 1. Композиция/декомпозиция 2. Можем пользоваться только stateless компонентами 3.

    HOCs позволяют создавать нечто похожее на декораторы и добавлять примеси в компонент 4. Небольшие утилиты HOCs могут быть скомпонованы в большие и полезные HOCs 35 35
  17. 37

  18. 38

  19. 41

  20. 41

  21. class Status extends Component { state = { isToggle: false

    }; toggle = () => { const { isToggle } = this.state; this.setState({ isToggle: !isToggle }) }; render() { const { isToggle } = this.state; const { status } = this.props; return ( <span onClick={this.toggle} > { status } { isToggle && <StatusList /> } </span> ); }; } 43 class Tooltip extends Component { state = { isToggle: false }; show = () => { this.setState({ isToggle: true }) }; hide = () => { this.setState({ isToggle: false }) }; render() { const { isToggle } = this.state; const { children, text } = this.props; return ( <span onMouseEnter={this.show} onMouseLeave={this.hide}> { isToggle && <div className="Tooltip">{ text }</div>} <span>{ children }</span> </span> ); }; }
  22. const withToggle = compose( // теперь используем withState & withHandlers

    в методе compose withState('toggledOn', 'toggle', false), withHandlers({ // withHandlers принимает объект обработчиков событий show: ({ toggle }) => (e) => toggle(true), hide: ({ toggle }) => (e) => toggle(false), toggle: ({ toggle }) => (e) => toggle((current) => !current) }) ) const Status = withToggle(({ status, toggledOn, toggle }) => <span onClick={ toggle }> { status } { toggledOn && <StatusList /> } </span> ); const Tooltip = withToggle(({ text, children, toggledOn, show, hide }) => <span> { toggledOn && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ show } onMouseLeave={ hide }>{ children }</span> </span> ); Before Using Hooks 44 44
  23. After Using Hooks 45 45 const Status =({ status })

    => { const [ state, setState ] = useState(false); return ( <span onClick={ () => setState(!state) }> { status } { state && <StatusList /> } </span> ) } const Tooltip = ({ text, children }) => { const [ state, setState ] = useState(false); return ( <span> { state && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ () => setState(true) } onMouseLeave={ () => setState(false) }>{ children }</span> </span> ); }
  24. After Using Hooks 46 46 const Status =({ status })

    => { const [ state, setState ] = useState(false); return ( <span onClick={ () => setState(!state) }> { status } { state && <StatusList /> } </span> ) } const Tooltip = ({ text, children }) => { const [ state, setState ] = useState(false); return ( <span> { state && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ () => setState(true) } onMouseLeave={ () => setState(false) }>{ children }</span> </span> ); }
  25. After Using Hooks 47 47 const useHook = (initialState) =>

    { const [state, setState] = useState(initialState); const show = () => setState(true); const hide = () => setState(false); const toggle = () => setState(!state); return [state, { show, hide, toggle }] } const Status = ({ status }) => { const [state, { toggle }] = useHook(false); return ( <span onClick={ toggle }> { status } { state && <StatusList /> } </span> ); } const Tooltip = ({ text, children }) => { const [state, { show, hide }] = useHook(false); return ( <span> { state && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ show } onMouseLeave={ hide }>{ children }</span> </span> ); }
  26. After Using Hooks 48 48 const useHook = (initialState) =>

    { const [state, setState] = useState(initialState); const show = () => setState(true); const hide = () => setState(false); const toggle = () => setState(!state); return [state, { show, hide, toggle }] } const Status = ({ status }) => { const [state, { toggle }] = useHook(false); return ( <span onClick={ toggle }> { status } { state && <StatusList /> } </span> ); } const Tooltip = ({ text, children }) => { const [state, { show, hide }] = useHook(false); return ( <span> { state && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ show } onMouseLeave={ hide }>{ children }</span> </span> ); }
  27. After Using Hooks 49 49 const useHook = (initialState) =>

    { const [state, setState] = useState(initialState); const show = () => setState(true); const hide = () => setState(false); const toggle = () => setState(!state); return [state, { show, hide, toggle }] } const Status = ({ status }) => { const [state, { toggle }] = useHook(false); return ( <span onClick={ toggle }> { status } { state && <StatusList /> } </span> ); } const Tooltip = ({ text, children }) => { const [state, { show, hide }] = useHook(false); return ( <span> { state && <div className="Tooltip">{ text }</div> } <span onMouseEnter={ show } onMouseLeave={ hide }>{ children }</span> </span> ); }
  28. 51 Вывод 1. Композиция/декомпозиция 2. Меньше кода 3. Избавились от

    Wrapper Hell-a 4. Небольшие утилиты в хуках могут быть скомбинированы в собственные хуки 51