recompacting your react application
TRANSCRIPT
![Page 1: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/1.jpg)
Greg Bergé, @neoziro
Recompacting your React application
![Page 2: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/2.jpg)
• From mixins to higher-order components
• Higher-order components
• Debugging & performances
![Page 3: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/3.jpg)
From mixins to higher-order components
![Page 4: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/4.jpg)
React v0.3.0 First public release of React,
with mixins support.
29/03/2013
🗓
![Page 5: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/5.jpg)
– 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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/6.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/7.jpg)
I want to reuse the back logic :)
![Page 8: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/8.jpg)
Let’s create a mixin!
![Page 9: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/9.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/10.jpg)
Let’s put the rendering logic inside!
![Page 11: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/11.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/12.jpg)
And now in real life…
![Page 13: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/13.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/14.jpg)
OK let’s refactor it.
😰
![Page 15: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/15.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/16.jpg)
A wild component appears!
‼
![Page 17: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/17.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/18.jpg)
Mixins
Name clashingHard refactoring
Complexity
😫
![Page 19: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/19.jpg)
10/03/2015React v0.13.0
Support for using ES6 classes to build React components.
🗓
![Page 20: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/20.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/21.jpg)
No mixin support.
![Page 22: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/22.jpg)
Let’s try inheritance!
💡
![Page 23: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/23.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/24.jpg)
I want it to be pure!
![Page 25: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/25.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/26.jpg)
Not every time…
![Page 27: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/27.jpg)
We don’t need a hierarchy, we need a composability.
😰
![Page 28: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/28.jpg)
React Blog post Mixins Considered harmful
by Dan Abramov
13/07/2016
🗓
![Page 29: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/29.jpg)
For the first time, “higher-order components”
concept is mentioned on React website.
😳
![Page 30: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/30.jpg)
What is a higher-order component?
![Page 31: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/31.jpg)
Component => EnhancedComponent
![Page 32: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/32.jpg)
Do you know Redux?
![Page 33: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/33.jpg)
connect(state => state)(TodoApp)
![Page 34: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/34.jpg)
Higher-order components step by step
![Page 35: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/35.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/36.jpg)
![Page 37: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/37.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/38.jpg)
1. The View
![Page 39: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/39.jpg)
const View = ({ onChange, value }) => <input onChange={onChange} value={value} />
![Page 40: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/40.jpg)
![Page 41: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/41.jpg)
const View = ({ onChange, value }) => <input onChange={onChange} value={value} />
![Page 42: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/42.jpg)
const View = 'input'
![Page 43: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/43.jpg)
2. The Model
![Page 44: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/44.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/45.jpg)
❌
My model is not generic
![Page 46: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/46.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/47.jpg)
More generic?
![Page 48: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/48.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/49.jpg)
Recomp(act|ose)
![Page 50: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/50.jpg)
const model = recompact.withState('value', 'onChange', ‘')
![Page 51: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/51.jpg)
const model = recompact.withState('value', 'onChange', ‘')
const MyInput = model('input')
![Page 52: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/52.jpg)
😒
![Page 53: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/53.jpg)
2. The Controller
![Page 54: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/54.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/55.jpg)
More generic?
![Page 56: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/56.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/57.jpg)
![Page 58: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/58.jpg)
const controller = recompact.withHandlers({ onChange: ({ onChange }) => ({ target: { value }}) => onChange(value),})
![Page 59: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/59.jpg)
const MyInput = recompact.compose( recompact.withState('value', 'onChange', ''), recompact.withHandlers({ onChange: ({ onChange }) => ({ target: { value }}) => onChange(value), }),)('input')
![Page 60: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/60.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/61.jpg)
OK, you won 3 lines…
![Page 62: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/62.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/63.jpg)
![Page 64: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/64.jpg)
Debugging & performances
![Page 65: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/65.jpg)
![Page 66: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/66.jpg)
![Page 67: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/67.jpg)
Recompose
![Page 68: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/68.jpg)
![Page 69: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/69.jpg)
isReferentiallyTransparentFunctionComponent
![Page 70: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/70.jpg)
What is a referentially transparent component?
• Function
• No default props
• No context
![Page 71: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/71.jpg)
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](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/72.jpg)
Recompact
![Page 73: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/73.jpg)
![Page 74: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/74.jpg)
What are we really doing?
![Page 75: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/75.jpg)
We take some props and return (or not) other props.
![Page 76: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/76.jpg)
(next, props) => next(props)
![Page 77: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/77.jpg)
subscribe, next… it reminds me of something
![Page 78: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/78.jpg)
props$ => props$
![Page 79: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/79.jpg)
const mapProps = propsMapper => (next, props) => next(propsMapper(props))
const mapProps = propsMapper => props$ => props$.map(propsMapper)
![Page 80: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/80.jpg)
Higher-order components
Stream of props
![Page 81: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/81.jpg)
Debugging is logging
![Page 82: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/82.jpg)
export default recompact.compose( recompact.withProps({ foo: 'bar' }), recompact.debug(), recompact.renameProp('foo', 'className'), recompact.debug(),)('input')
![Page 83: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/83.jpg)
• Avoid mixins and inheritance
• Separate concerns using higher-order components
• Create small higher-order components and compose them
![Page 84: Recompacting your react application](https://reader033.vdocuments.site/reader033/viewer/2022042723/58f163521a28ab313a8b45ad/html5/thumbnails/84.jpg)
Thanks!