recompacting your react application

84
Greg Bergé, @neoziro Recompacting your React application

Upload: greg-berge

Post on 15-Apr-2017

103 views

Category:

Technology


0 download

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!