Top Banner
Greg Bergé, @neoziro Recompacting your React application
84

Recompacting your react application

Apr 15, 2017

Download

Technology

Greg Bergé
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Recompacting your react application

Greg Bergé, @neoziro

Recompacting your React application

Page 2: Recompacting your react application

• From mixins to higher-order components

• Higher-order components

• Debugging & performances

Page 3: Recompacting your react application

From mixins to higher-order components

Page 4: Recompacting your react application

React v0.3.0 First public release of React,

with mixins support.

29/03/2013

🗓

Page 5: Recompacting your react application

– from React documentation

“Sometimes very different components may share some common functionality. These are sometimes called cross-cutting concerns.”

Page 6: Recompacting your react application

React.createClass({ handleClick: function (event) { event.preventDefault(); window.history.back(); } render: function () { return <a onClick={this.handleClick}>Back</a>; }});

Page 7: Recompacting your react application

I want to reuse the back logic :)

Page 8: Recompacting your react application

Let’s create a mixin!

Page 9: Recompacting your react application

const BackMixin = { handleClick: function (event) { event.preventDefault(); window.history.back(); }};

React.createClass({ mixins: [BackMixin], render: function() { return <a onClick={this.handleClick}>Back</a>; }});

Page 10: Recompacting your react application

Let’s put the rendering logic inside!

Page 11: Recompacting your react application

const BackMixin = { handleClick: function (event) { event.preventDefault(); window.history.back(); }

renderBackButton: function () { return <a onClick={this.handleClick}>Back</a>; }};

React.createClass({ mixins: [BackMixin], render: function() { return this.renderBackButton(); }});

Page 12: Recompacting your react application

And now in real life…

Page 13: Recompacting your react application

React.createClass({ mixins: [BackMixin, RouterMixin],

handleClick: function (event) { event.preventDefault(); this.transitionTo('home'); } render: function() { return ( <div> <a onClick={this.handleClick}>Go home</a> {this.renderBackButton()} </div> ); }});

Page 14: Recompacting your react application

OK let’s refactor it.

😰

Page 15: Recompacting your react application

const BackMixin = { handleBack: function (event) { event.preventDefault(); window.history.back(); }

renderBackButton: function () { return <a onClick={this.handleBack}>Back</a>; }};

Page 16: Recompacting your react application

A wild component appears!

Page 17: Recompacting your react application

React.createClass({ mixins: [BackMixin, PureRenderMixin, RouterMixin, ForwardMixin],

render: function() { return ( <div> <a onClick={this.handleClick}>Go back</a> </div> ); }});

I forgot this one

Page 18: Recompacting your react application

Mixins

Name clashingHard refactoring

Complexity

😫

Page 19: Recompacting your react application

10/03/2015React v0.13.0

Support for using ES6 classes to build React components.

🗓

Page 20: Recompacting your react application

class extends React.Component { handleClick = (event) => { event.preventDefault(); window.history.back(); }; render() { return <a onClick={this.handleClick}>Back</a>; }}

Page 21: Recompacting your react application

No mixin support.

Page 22: Recompacting your react application

Let’s try inheritance!

💡

Page 23: Recompacting your react application

class BackComponent extends React.Component { handleClick = (event) => { event.preventDefault(); window.history.back(); };}

class extends BackComponent { render() { return <a onClick={this.handleClick}>Back</a>; }}

Page 24: Recompacting your react application

I want it to be pure!

Page 25: Recompacting your react application

class BackComponent extends React.PureComponent { handleClick = (event) => { event.preventDefault(); window.history.back(); };}

class extends BackComponent { render() { return <a onClick={this.handleClick}>Back</a>; }}

Page 26: Recompacting your react application

Not every time…

Page 27: Recompacting your react application

We don’t need a hierarchy, we need a composability.

😰

Page 28: Recompacting your react application

React Blog post Mixins Considered harmful

by Dan Abramov

13/07/2016

🗓

Page 29: Recompacting your react application

For the first time, “higher-order components”

concept is mentioned on React website.

😳

Page 30: Recompacting your react application

What is a higher-order component?

Page 31: Recompacting your react application

Component => EnhancedComponent

Page 32: Recompacting your react application

Do you know Redux?

Page 33: Recompacting your react application

connect(state => state)(TodoApp)

Page 34: Recompacting your react application

Higher-order components step by step

Page 35: Recompacting your react application

class extends Component { state = { value: '' };

handleChange = ({ target: { value } }) => this.setState({ value }); render() { return ( <input onChange={this.handleChange} value={this.state.value} /> ) }}

Page 36: Recompacting your react application
Page 37: Recompacting your react application

class extends Component { state = { value: '' };

handleChange = ({ target: { value } }) => this.setState({ value }); render() { return ( <input onChange={this.handleChange} value={this.state.value} /> ) }}

Model

Controller

View

Page 38: Recompacting your react application

1. The View

Page 39: Recompacting your react application

const View = ({ onChange, value }) => <input onChange={onChange} value={value} />

Page 40: Recompacting your react application
Page 41: Recompacting your react application

const View = ({ onChange, value }) => <input onChange={onChange} value={value} />

Page 42: Recompacting your react application

const View = 'input'

Page 43: Recompacting your react application

2. The Model

Page 44: Recompacting your react application

const Model = class extends Component { state = { value: '' }

handleChange = value => this.setState({ value })

render() { return ( <View onChange={({ target: { value } }) => handleChange(value)} value={this.state.value} /> ) }}

Page 45: Recompacting your react application

My model is not generic

Page 46: Recompacting your react application

const model = BaseComponent => class extends Component { state = { value: '' }

handleChange = value => this.setState({ value })

render() { return ( <BaseComponent {...this.props} onChange={this.handleChange} value={this.state.value} /> ) }}

Page 47: Recompacting your react application

More generic?

Page 48: Recompacting your react application

const withState = (stateName, handlerName, initialValue) => BaseComponent => class extends Component { state = { [stateName]: initialValue }

handleChange = value => this.setState({ [stateName]: value })

render() { return ( <BaseComponent {...this.props} {...{ [stateName]: this.state[stateName], [handlerName]: this.handleChange, }} /> ) } }

const model = withState('value', 'onChange', '')

Page 49: Recompacting your react application

Recomp(act|ose)

Page 50: Recompacting your react application

const model = recompact.withState('value', 'onChange', ‘')

Page 51: Recompacting your react application

const model = recompact.withState('value', 'onChange', ‘')

const MyInput = model('input')

Page 52: Recompacting your react application

😒

Page 53: Recompacting your react application

2. The Controller

Page 54: Recompacting your react application

const controller = BaseComponent => class extends Component { handleChange = ({ target: { value } }) => this.props.onChange(value); render() { return ( <BaseComponent {...this.props} onChange={handleChange} /> ) } }

Page 55: Recompacting your react application

More generic?

Page 56: Recompacting your react application

const withHandlers = config => BaseComponent => class extends Component { constructor(props) { super(props) this.handlers = {} Object.keys(config).forEach((key) => { this.handlers[key] = (...args) => config[key](this.props)(...args); }) } render() { return ( <BaseComponent {...this.props} {...this.handlers} /> ) } }

const controller = withHandlers({ onChange: ({ onChange }) => ({ target: { value }}) => onChange(value),})

Page 57: Recompacting your react application
Page 58: Recompacting your react application

const controller = recompact.withHandlers({ onChange: ({ onChange }) => ({ target: { value }}) => onChange(value),})

Page 59: Recompacting your react application

const MyInput = recompact.compose( recompact.withState('value', 'onChange', ''), recompact.withHandlers({ onChange: ({ onChange }) => ({ target: { value }}) => onChange(value), }),)('input')

Page 60: Recompacting your react application

class extends Component { state = { value: '' };

handleChange = ({ target: { value } }) => this.setState({ value }); render() { return ( <input onChange={this.handleChange} value={this.state.value} /> ) }}

Page 61: Recompacting your react application

OK, you won 3 lines…

Page 62: Recompacting your react application

const MyBluePerfInput = recompact.compose( // Performance recompact.pure, // Model recompact.withState('value', 'onChange', ''), // Controller recompact.withHandlers({ onChange: ({ onChange }) => ({ target: { value } }) => onChange(value), }), // Style recompact.withProps({ style: { color: ‘blue’ } }),)('input')

Page 63: Recompacting your react application
Page 64: Recompacting your react application

Debugging & performances

Page 65: Recompacting your react application
Page 66: Recompacting your react application
Page 67: Recompacting your react application

Recompose

Page 68: Recompacting your react application
Page 69: Recompacting your react application

isReferentiallyTransparentFunctionComponent

Page 70: Recompacting your react application

What is a referentially transparent component?

• Function

• No default props

• No context

Page 71: Recompacting your react application

const MyBluePerfInput = recompact.compose( // No transparent recompact.pure, // No transparent recompact.withState('value', 'onChange', ''), // No transparent recompact.withHandlers({ onChange: ({ onChange }) => ({ target: { value } }) => onChange(value), }), // Transparent recompact.withProps({ style: { color: ‘blue’ } }),)('input')

Page 72: Recompacting your react application

Recompact

Page 73: Recompacting your react application
Page 74: Recompacting your react application

What are we really doing?

Page 75: Recompacting your react application

We take some props and return (or not) other props.

Page 76: Recompacting your react application

(next, props) => next(props)

Page 77: Recompacting your react application

subscribe, next… it reminds me of something

Page 78: Recompacting your react application

props$ => props$

Page 79: Recompacting your react application

const mapProps = propsMapper => (next, props) => next(propsMapper(props))

const mapProps = propsMapper => props$ => props$.map(propsMapper)

Page 80: Recompacting your react application

Higher-order components

Stream of props

Page 81: Recompacting your react application

Debugging is logging

Page 82: Recompacting your react application

export default recompact.compose( recompact.withProps({ foo: 'bar' }), recompact.debug(), recompact.renameProp('foo', 'className'), recompact.debug(),)('input')

Page 83: Recompacting your react application

• Avoid mixins and inheritance

• Separate concerns using higher-order components

• Create small higher-order components and compose them

Page 84: Recompacting your react application

Thanks!