react & the art of managing complexity

93
React & The Art of Managing Complexity

Upload: ryan-anklam

Post on 12-Aug-2015

331 views

Category:

Technology


0 download

TRANSCRIPT

React & The Art of Managing Complexity

UI Engineer @ [email protected]@gmail.com

Complexity

http://photo.sf.co.ua/id41

Complexity

http://images.fineartamerica.com/images-medium-large/soyuz-tma-spacecraft-cockpit-detlev-van-ravenswaay.jpg

Complexity is using the wrong abstraction.

MVC is the wrong abstraction.

MVC is the wrong abstraction.

Small, single purpose components are the right abstraction.

Welcome to the composable web!

What if I told you that markup, events, and handlers should live side by side inside the component?

What if I told you that markup, events, and handlers should live side by side inside the component?

Warning, ES6 ahead!

http://kangax.github.io/compat-table/es6/

class CurrentTime extends React.Component{ constructor( props ){ super( props ); this.state = { clickCount : 0 }; } handleClick(){ this.setState( { clickCount : this.state.clickCount + 1 } ); } render(){ return ( <button onClick={this.handleClick.bind( this )}> Clicked {this.state.clickCount} times </button> ); }};

class CurrentTime extends React.Component{ constructor(){ super(); this.state = { clickCount : 0 }; } handleClick(){ this.setState( { clickCount : this.state.clickCount + 1 } ); } render(){ return ( <button onClick={this.handleClick.bind( this )}> Clicked {this.state.clickCount} times </button> ); }};

ZOMG!!! Theres MARKUP in JavaScript!!!

class CurrentTime extends React.Component{ constructor(){ super(); this.state = { clickCount : 0 }; } handleClick(){ this.setState( { clickCount : this.state.clickCount + 1 } ); } render(){ return ( <button onClick={this.handleClick.bind( this )}> Clicked {this.state.clickCount} times </button> ); }};

Theres an onClick in your markup!!!

class CurrentTime extends React.Component{ constructor(){ super(); this.state = { clickCount : 0 }; } handleClick(){ this.setState( { clickCount : this.state.clickCount + 1 } ); } render(){ return ( <button onClick={this.handleClick.bind( this )}> Clicked {this.state.clickCount} times </button> ); }};

1994 Called, it want’s it’s JavaScript Back!

class CurrentTime extends React.Component{ constructor(){ super(); this.state = { clickCount : 0 }; } handleClick(){ this.setState( { clickCount : this.state.clickCount + 1 } ); } render(){ return ( <button onClick={this.handleClick.bind( this )}> Clicked {this.state.clickCount} times </button> ); }};

Go home Ryan, you’re drunk.

Your markup and JavaScript are inherently tightly coupled.

There is a cost to context switching, even switching from a “model” to a “view”.

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

foo controller

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

module{}

foo controller

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

module{}

function(){}

foo controller

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

module{}

function(){}

function(){}

foo controller

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

module{}

function(){}

function(){}

property: value

foo controller

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

module{}

function(){}

function(){}

property: value

function(){}

foo controller

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

module{}

function(){}

function(){}

property: value

function(){}

object{}

foo controller

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

foo view

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

foo view

<markup>

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

foo view

<markup>

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

<markup>

foo view

<markup>

bind:action

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

<markup>

http://commons.wikimedia.org/wiki/File:Programmer_writing_code_with_Unit_Tests.jpg

class CurrentTime extends React.Component{ constructor( props ){ super( props ); this.state = { clickCount : 0 }; } handleClick(){ this.setState( { clickCount : this.state.clickCount + 1 } ); } render(){ return ( <button onClick={this.handleClick.bind( this )}> Clicked {this.state.clickCount} times </button> ); }};

class CurrentTime extends React.Component{ constructor( props ){ super( props ); this.state = { clickCount : 0 }; } handleClick(){ this.setState( { clickCount : this.state.clickCount + 1 } ); } render(){ return ( <button onClick={this.handleClick.bind( this )}> Clicked {this.state.clickCount} times </button> ); }};

What does this component look like?

Clicked 2 times

Complexity is the inability to quickly reason about where application state is being changed.

Controller ViewModel

Controller View

View

Model

Controller View

View

Model

Controller View

View

Controller View

Model

Controller View

View

Controller View

Model

Controller View

View

Controller View

Model

Controller View

View

Controller View

Model

Controller View

View

Controller View

Model

Controller View

View

Controller View

Model

Controller View

View

Controller View

Controller View

Model

Controller View

View

Controller View

Controller View

Model

Controller View

View

Controller View

Controller View

Model

Model

Model

Controller View

View

Controller View

Controller View

Theres a bug on our account page.

Model

Model

Model

Complexity is…mutability.

“The last thing you wanted any programmer to do is mess with internal state…” Allan Kay - Creator of

Smalltalk

Immutability is the path to managing complexity.

https://www.youtube.com/watch?v=I7IdS-PbEgI

In React, a component’s props are immutable.

class Movie extends React.Component{ constructor( props ){ super( props ); } render(){ return ( <div className="movie"> {this.props.name} </div> ); }};

Movie.defaultProps = { name: 'Batman' };

React.render( <Movie name="The Dark Knight"/>, document.body );

React.render( <Movie name="The Dark Knight"/>, document.body );

Here be Dragons?

React.render(<Movie name={['The Dark Knight']}/>, document.body);

Here be Dragons?

Unpredictability is hidden complexity.

Remove that hidden complexity with PropTypes.

class Movie extends React.Component{ constructor( props ){ super( props ); } render(){ return ( <div className="movie"> {this.props.name} </div> ); }};

Movie.propTypes = { name: React.PropTypes.string };Movie.defaultProps = { name: 'Batman' };

An application without any state changes would be pretty boring.

A React component’s state property is mutable.

class CurrentTime extends React.Component{ constructor( props ){ super( props ); this.state = { currentTime : new Date() }; } componentDidMount(){ this.interval = setInterval(() => { this.setState( { currentTime : new Date() } ); }, 1000 ); } render(){ return ( <div>{this.state.currentTime.toString()}</div> ); }};

Idiomatic React encourages using props as much as possible.

How do we make our app interesting, while embracing props?

class CurrentTime extends React.Component{ constructor( props ){ super( props ); } render(){ return ( <div>{this.props.currentTime}</div> ); }};

class CurrentTimeController extends React.Component{ constructor(){ super(); this.state = { currentTime : new Date() }; } componentDidMount(){ this.interval = setInterval( () => { this.setState( { currentTime : new Date() } ); }, 1000 ); } render(){ return ( <CurrentTime currentTime={this.state.currentTime.toString()} /> ); }}

What about Events?

class FormController extends React.Component{ constructor( props ){ super( props ); this.state = { name : '' }; } nameChangeHandler( value ){ this.setState( { name : value } ); } render(){ return ( <div> Hello, {this.state.name}

<NameInput name={this.state.name} changeHandler={this.nameChangeHandler.bind(this)}/>

</div> ); }}

“There’s a bug on our account page, US users are seeing € instead of $ for their plan price.”

//controller component for the account sectionclass AccountController extends React.Component{ constructor( props ){ super( props ); } render(){ return( <PlanInfo

streamingInfo={this.props.streamingInfo} region={this.props.region} />

); }}

class PlanInfo extends React.Component{ constructor( props ){ super( props ) console.log( props ); } getStreamingSection(){ if( this.props.streamingInfo ){ let streamingInfo = this.props.streamingInfo; return ( <StreamingSection streams={streamingInfo.streams} price={streamingInfo.price} region={this.props.region/> ); } } render(){ let streamingPlan = this.getStreamingSection(); return ( <div> {streamingPlan} </div> ); }}

class StreamingSection extends React.Component{ constructor( props ){ super( props ); } getFormattedPrice(){ let symbol =

( this.props.region === "US" ) ? '€' : '$';

return `${symbol} {this.props.price}`; } render(){ return ( <article> <p>Streams: {`${this.props.streams} streams` }</p> <p>Price: {this.getFormattedPrice()}</p> </article> ); }}

Why was that easy?

Each component was small.

Each component did a single thing.

All the data was in immutable.

Sprinkles are for those who have code in prod.

http://www.dallasmomsanddads.com/wp-content/uploads/2013/04/photo-1.jpg

<div className={pinEntryClass}>

{accountVerify}

<h2 className="account-subheader parent-control-subheader"> {this.i18n['pin.page.subheader']} </h2> <p>{this.i18n['pin.page.info']}</p>

<PinPad handleValidPinEntry={this.handleValidPinEntry} handleInvalidPinEntry={this.handleInvalidPinEntry} handleValidNumberEntry={this.handleValidNumberEntry} ref="pinPad" pin={this.state.pin} enabled={this.state.pinEnabled} />

...

var pinProps = { enabled: true, handleValidNumberEntry: function(pin) {

newPin = pin; }, handleValidPinEntry: function() { errorMessage.addClass('hidden'); buttonSave[0].disabled = false; }, handleInvalidPinEntry: function() { errorMessage.removeClass('hidden'); buttonSave[0].disabled = true; } };

React.render(pinPad(pinProps), document.querySelector('.pin-pad-container'));

Now what?

Write some code!http://facebook.github.io/react/docs/tutorial.html

jsbin.com

We are doing some pretty cool stuff with JavaScript at Netflix. Please stop me in the halls

I’d love to share more of what we are doing!

Thank You! @bittersweetryan