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

Recompose to simplicity and beyond

Recompose to simplicity and beyond

A presentation about the basics of HoC, Presentational Layers, Container Components, but mostly recompose and how to structure projects in a more functional way.

Timo Obereder

June 22, 2017
Tweet

More Decks by Timo Obereder

Other Decks in Programming

Transcript

  1. RECOMPOSE To Simplicity And Beyond 22.June 2017 – React JS

    Meetup Vienna Timo Obereder Android Native-/React Web- Developer @defuex
  2. Application: The process of applying a function to its arguments

    in order to produce a return value. Partial Application: The process of applying a function to some of its arguments. It takes a function with multiple parameters and returns a function with fewer parameters. Add(x,y) = x + y Add2 = Partial(Add, 7)
  3. Curry: A function that takes a function with multiple parameters

    as input and returns a function with exactly one parameter. f(x,y,z) curried to f:x => (y => (z => n)))
  4. react-redux (arg1, arg2) => (Component) => EnhancedComponent const EnhancedComponent =

    connect( mapStateToProps, mapDispatchToProps, )(Component);
  5. (Component, arg1) => EnhancedComponent relay export default Relay.createContainer(Todo, { fragments:

    { todo: () => Relay.QL` fragment on Todo { complete, id, text }` } });
  6. Yes

  7. But

  8. const Commit = ({ sha, message }) => ( <div>

    <p>{sha}</p> <p>{message}</p> </div> )
  9. class CommitContainer extends React.Component { state = { commit: null

    }; componentDidMount() { fetch(`/commits/${this.props.id}`) .then(commit => this.setState({ commit })); } render() { const { commit } = this.state; return <Commit {...this.props} {...commit} />; } }
  10. const fetchCommit = (WrappedComponent) => ( class extends React.Component {

    state = { commit: null }; componentDidMount() { fetch(`/commits/${this.props.id}`) .then(commit => this.setState({ commit })); } render() { const { commit } = this.state; return <WrappedComponent {...this.props} {...commit} />; } } ) const CommitContainer = fetchCommit(Commit);
  11. const fetchResource = (path) => (WrappedComponent) => ( class extends

    React.Component { state = { resource: null }; componentDidMount() { path(this.props) .then(resource => this.setState({ resource })); } render() { const { resource } = this.state; return <WrappedComponent {...this.props} {...resource} />; } } ) const fetchPost = fetchResource(props => fetch(`/commits/${props.id}`)); const CommitContainer = fetchPost(Commit);
  12. Props Proxy const withUser = (WrappedComponent) => { return class

    extends React.Component { render() { const user = { id: ’1337', name: Pacman' }; return ( <WrappedComponent {...this.props} user={user} /> ); } } } <Props manipulation>
  13. Inheritance Inversion <Render hijacking> function authLocked(WrappedComponent) { return class Enhancer

    extends WrappedComponent { render() { if (this.props.loggedIn) { return super.render() } else { return null } } } }
  14. Inheritance Inversion <State manipulation> export function debug(WrappedComponent) { return class

    Enhancer extends WrappedComponent { render() { return ( <div> <h2>Debugger Component</h2> <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre> <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre> {super.render()} </div> ) } } }
  15. const BaseComponent = (props) => {...} let EnhancedComponent = firstHoc(BaseComponent);

    EnhancedComponent = secondHoc(/*...args*/)(EnhancedComponent); EnhancedComponent = thirdHoc(/*...args*/)(EnhancedComponent);
  16. function compose(...funcs) { if (funcs.length === 0) { return arg

    => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] return (...args) => { let result = last(...args) for (let i = funcs.length - 2; i >= 0; i--) { const f = funcs[i] result = f(result) } return result } }
  17. „recompose is a react utility belt for function components and

    higher-order components“ – Andrew Clark
  18. const withCounterState = withState('counter', 'setCounter', 0) const Counter = ({

    counter, setCounter, }) => ( <div> Count: {counter} <button onClick={() => setCounter(n => n + 1)}>Increment</button> <button onClick={() => setCounter(n => n - 1)}>Decrement</button> </div> ) export default compose( withCounterState, )(Counter); withState
  19. withHandlers const withCounterHandler = withHandlers({ handleCounterUp : ({ counter, setCounter,

    }) => () => { setCounter(counter + 1); }, handleCounterDown : ({ counter, setCounter, }) => () => { setCounter(counter - 1); }, });
  20. withState & withHandlers const Counter = ({ counter, handleCounterUp, handleCounterDown,

    }) => ( <div> Count: {counter} <button onClick={handleCounterUp}>Increment</button> <button onClick={handleCounterDown}>Decrement</button> </div> ) export default compose( withCounterState, withCounterHandler, )(Counter);
  21. shouldUpdate const withCommitsAmountChanged = shouldUpdate( (props, nextProps) => { if(

    (props.commitsMax !== nextProps.commitsMax) || (props.someOtherProp !== nextProps.someOtherProp) ){ return true; } return false; });
  22. Cons Expensive to change if abstraction is wrong Could be

    complex for newcomers(pretty functional) „More complex computation"