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

React HOC and React Hooks

React Moscow
December 11, 2019

React HOC and React Hooks

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

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

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