SlideShare uma empresa Scribd logo
1 de 60
Baixar para ler offline
Structuring React
components
Bartek Witczak
What do you mean by
structuring?
What do you mean by
structuring?
• single component in isolation
• relation between components
• structuring modules
• whole page architecture
What do you mean by
structuring?
• single component in isolation
• relation between components
• structuring modules
• whole page architecture
What’s the purpose?
What’s the purpose?
Product Library
What’s the purpose?
Product Library
React component
Props State
Lifecycle event
ContextRender
class WelcomeTo4Devs extends React.Component {
render () {
return 'Welcome to meme generator!'
}
}
(Functional) Stateless
Props State
Lifecycle event
ContextRender
const Loading = () => (
<div className='loading'>
<i className='icon-refresh spin’/>
</div>
)
Functional stateless
Functional stateless
const Loading = ({ text }) => (
<div className='loading'>
<i className='icon-refresh spin'/>
<div className='loading-text'>
{text}
</div>
</div>
)
Stateful
Props State
Lifecycle event
ContextRender
class UsersSelect extends React.Component {
constructor(props) {
super(props)
this.state = { loading: true, options: [] }
}
componentDidMount() {
UsersService.getAll().then(options => {
this.setState({ loading: false, options })
})
}
render() {
const { selected, onSelect } = this.props
const { loading, options } = this.state
return (
<Select
value={selected}
isLoading={loading}
options={options}
onChange={onSelect}
/>
)
}
}
Container & Presentational
Smart & Dumb
View & Controller
Container & Presentational
Props State
Lifecycle event
ContextRender
Props State
Lifecycle event
ContextRender
const UsersSelect = ({
onSelect,
options,
selected,
}) => (
<Select
value={selected}
options={options}
onChange={onSelect}
/>
)
Presentational
const mapStateToProps = state => ({
options: state.users.options,
selected: state.users.selected,
})
const mapDispatchToProps = dispatch => ({
onSelect: options => dispatch({
type: 'SELECT_USER',
options,
})
})
const Users = connect
(mapStateToProps, mapDispatchToProps)
(UsersSelect)
Container
Higher order component
const withLoading = WrappedComponent => ({ loading, ...props }) => {
if ( loading ) {
return <div>Keep calm and eat broccoli ...</div>
} else {
return <WrappedComponent {...props} />
}
}
Coupling & cohesion
Coupling
Dependency between elements
( components)
===
changing one
component does
not imply changes in
other
Loose coupling Tight coupling
changing one
component implies
changes in other
Cohesion
if element’s responsibilities form
one thing
===
single
responsibility
High cohesion Low cohesion
multiple
responsibilities
Loosely coupled
&
high cohesive component
Team
John Mike Tom
Challenge:
Create meme details panel
{
name: 'Challenge accepted',
file: 'challenge-accepted.jpg',
date: '2018-04-05T11:15:30',
creator: 'Jim Beam’,
}
const MemeDetails = ({ meme }) => (
<div className='meme-details'>
<div>Name: <span>{meme.name}</span></div>
<div>File: <span>{meme.file}</span></div>
<div>Date: <span>{format(meme.date, 'YYYY-MM-DD')}</span></div>
<div>Creator: <span>{meme.creator}</span></div>
</div>
)
John
...
<MemeDetails meme={meme} />
...
Mike
const MemeDetails = ({ name, file, date, creator }) => (
<div className='meme-details'>
<div>Name: <span>{name}</span></div>
<div>File: <span>{file}</span></div>
<div>Date: <span>{date}</span></div>
<div>Creator: <span>{creator}</span></div>
</div>
)
...
<MemeDetails
name={meme.name}
file={meme.file}
date={format(meme.date, ‘YYYY-MM-DD’)}
creator={meme.creator}
/>
...
Tom
const MemeDetails = ({ fields }) => (
<div className="meme-details">
{fields.map(f => (
<div>
{f.label}: <span>{f.value}</span>
</div>
))}
</div>
)
...
<MemeDetails
fields={[
{label: 'Name', value: meme.name},
{label: 'File', value: meme.file},
{label: 'Date', value: format(meme.date, 'YYYY-MM-DD')},
{label: 'Creator', value: meme.creator},
]}
/>
...
What about future
changes?
What about future
changes?
• changing model schema
const MemeDetails = ({ meme }) => (
<div className='meme-details'>
<div>Name: <span>{meme.name}</span></div>
<div>File: <span>{meme.file}</span></div>
<div>Format: <span>{meme.format}</span></div>
<div>Date: <span>{format(meme.date, 'YYYY-MM-DD')}</span></div>
<div>Creator: <span>{meme.creator}</span></div>
</div>
)
John
...
<MemeDetails meme={meme} />
...
Mike
const MemeDetails = ({ name, file, format, date, creator }) => (
<div className='meme-details'>
<div>Name: <span>{name}</span></div>
<div>File: <span>{file}</span></div>
<div>Format: <span>{format}</span></div>
<div>Date: <span>{date}</span></div>
<div>Creator: <span>{creator}</span></div>
</div>
)
...
<MemeDetails
name={meme.name}
file={meme.file}
format={meme.format}
date={format(meme.date, ‘YYYY-MM-DD’)}
creator={meme.creator}
/>
...
Tom
const MemeDetails = ({ fields }) => (
<div className="meme-details">
{fields.map(f => (
<div>
{o.label}: <span>{o.value}</span>
</div>
))}
</div>
)
...
<MemeDetails
fields={[
{label: 'Name', value: meme.name},
{label: 'File', value: meme.file},
{label: 'Format', value: meme.format},
{label: 'Date', value: format(meme.date, 'YYYY-MM-DD')},
{label: 'Creator', value: meme.creator},
]}
/>
...
What about future
changes?
• changing model schema
• formatting
const MemeDetails = ({ meme, dateFormat }) => (
<div className='meme-details'>
<div>Name: <span>{meme.name}</span></div>
<div>File: <span>{meme.file}</span></div>
<div>Date: <span>{format(meme.date, dateFormat)}</span></div>
<div>Creator: <span>{meme.creator}</span></div>
</div>
)
John
...
<MemeDetails meme={meme} meme=‘YYYY-MM-DD H:mm’/>
...
Mike
const MemeDetails = ({ name, file, date, creator }) => (
<div className='meme-details'>
<div>Name: <span>{name}</span></div>
<div>File: <span>{file}</span></div>
<div>Date: <span>{date}</span></div>
<div>Creator: <span>{creator}</span></div>
</div>
)
...
<MemeDetails
name={meme.name}
file={meme.file}
date={format(meme.date, ‘YYYY-MM-DD H:mm’)}
creator={meme.creator}
/>
...
Tom
const MemeDetails = ({ fields }) => (
<div className="meme-details">
{fields.map(f => (
<div>
{o.label}: <span>{o.value}</span>
</div>
))}
</div>
)
...
<MemeDetails
fields={[
{label: 'Name', value: meme.name},
{label: 'File', value: meme.file},
{label: 'Date', value:
format(meme.date, ‘YYYY-MM-DD H:mm’)},
{label: 'Creator', value: meme.creator},
]}
/>
...
Fight!
John Mike Tom
High cohesion
Tightly coupled with model
Single place when
modifying model
FormattingJohn
Loosely coupled with model
Formatting
Low cohesion
Many places when
modifying model
Mike
Very abstract
Loosely coupled with model
Low cohesion
Multiple places when
modifying model
Very abstract
Tom
And something from 

real world?
const OptionsList = ({ open, width, options = [], onOptionClick, range, height }) => !open || options.length === 0
? null
: (
<div style={{ width: width < 70 ? 70 : width, maxHeight: height }} className={classes2.optionsList}>
{
options.map(({ name, value, selected }, key) => (
<div
key={key}
className={classes2.optionsListItem + ' ' + (selected ? classes2.selected : '')}
onClick={e => {
e.stopPropagation()
onOptionClick(value, !selected)
}}>
{!range && <input type='checkbox' checked={selected} style={{ marginRight: '5px' }} />}
{name}
</div>
))
}
</div>
)
export class MultiSelect extends Component {
state = { optionsListOpen: false }
componentDidMount () {
const width = ReactDOM.findDOMNode(this).offsetWidth
this.setState({ width })
}
getInputText = () => {
const value = this.props.value || []
return value.length > 0 ? value.sort().join(', ') : 'Not selected'
}
onOptionClick = (selectedValue, selected) => {
const { onChange } = this.props
const value = this.props.value || []
onChange && onChange(selected
? [selectedValue, ...value]
: value.filter(item => selectedValue !== item)
)
}
toggleOptionsList = () => {
const { optionsListOpen } = this.state
this.setState({ optionsListOpen: !optionsListOpen })
}
prepareOptions = () => {
const { options } = this.props
const value = this.props.value || []
const preparedOptions = [...options]
value.forEach(selected => {
const optionIndex = preparedOptions.findIndex(({ value }) => value === selected)
if (optionIndex !== -1) preparedOptions[optionIndex] = { ...preparedOptions[optionIndex], selected: true }
})
return preparedOptions
}
close = () => this.setState({ optionsListOpen: false })
render () {
const { optionsListOpen, width } = this.state
const { options, inputSize, customStyle = {}, height } = this.props
return (
<ClickOutside onClickOutside={this.close}>
<div
className={classes.inputSelectForm}
style={{
position: 'relative',
display: 'flex',
height: inputSize || '44px',
alignItems: 'center',
...customStyle
}}
onClick={this.toggleOptionsList}>
<span style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
display: 'block',
fontSize: '15px'
}}>{this.getInputText()}</span>
<div style={{ position: 'absolute', right: 0}}>
<ChevronDown marginRight='0' />
</div>
<OptionsList
open={optionsListOpen}
width={width}
height={height}
options={this.prepareOptions()}
onOptionClick={this.onOptionClick} />
</div>
</ClickOutside>
)
}
}
export class RangeSelect extends Component {
state = { optionsListOpen: false, currentlyEditing: 'min' }
componentDidMount () {
const width = ReactDOM.findDOMNode(this).offsetWidth
this.setState({ width })
}
getParsedMinMax = () => ({
min: parseInt(this.props.min, 10),
max: parseInt(this.props.max, 10)
})
getInputText = () => {
const { min = 0, max = 0 } = this.getParsedMinMax()
if (!min && min !== 0) return '0'
return min === max ? max : `${min} - ${max}`
}
openOptionsList = () => {
const { optionsListOpen, currentlyEditing } = this.state
this.setState({ optionsListOpen: true })
}
prepareOptions = () => {
const { options } = this.props
const { min, max } = this.getParsedMinMax()
return options
.map(({ name, value }) => ({ name, value: parseInt(value, 10) }))
.map(option => ({ ...option, selected: option.value >= min && option.value <= max }))
}
onOptionClick = (selectedValue, selected) => {
const { onChange } = this.props
const { min, max } = this.getParsedMinMax()
const { currentlyEditing } = this.state
const parsedValue = parseInt(selectedValue, 10)
const newMinMax = { min: min.toString(), max: max.toString() }
if (currentlyEditing === 'min') {
newMinMax.min = parsedValue
newMinMax.max = parsedValue
this.setState({ currentlyEditing: 'max' })
} else {
if (parsedValue < min) {
newMinMax.max = min
newMinMax.min = parsedValue
} else {
newMinMax.max = parsedValue
}
this.setState({ currentlyEditing: 'min' })
}
onChange && onChange(newMinMax)
}
close = () => this.setState({ optionsListOpen: false, currentlyEditing: 'min' })
render () {
const { optionsListOpen, width } = this.state
const { options, inputSize, customStyle, height } = this.props
return (
<ClickOutside onClickOutside={this.close}>
<div
className={classes.inputSelectForm}
style={{
position: 'relative',
display: 'flex',
height: inputSize || '44px',
alignItems: 'center',
paddingRight: '0px',
...customStyle
}}
onClick={this.openOptionsList}>
<span style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
display: 'block',
fontSize: '15px'
}}>{this.getInputText()}</span>
<div style={{ position: 'absolute', right: 0}}>
<ChevronDown marginRight='0' />
</div>
<OptionsList
range
ref={optionsList => this.optionsList = optionsList}
open={optionsListOpen}
width={width}
height={height}
options={this.prepareOptions()}
onOptionClick={this.onOptionClick} />
</div>
</ClickOutside>
)
}
}
const typeInputs = {
text: (params, error, returnEvent) => (
<input
type='text'
{...params}
onChange={(e) => { (params && params.onChange) ? returnEvent ? params.onChange(e) : params.onChange(e.target.value) : null }}
className={`${classes.inputForm} ${error ? classes.inputError : ''} ${params.disabled ? classes.inputDisabled : ''} ${ (Object.keys(params.emptyStyle || {}).length && (!params.value || params.value === NOT_SELECTED)) ? classes.emptyInput : null }`} />
),
select: (params = {}, error) => {
const { options = [], onChange, prependNullOption, emptyStyle = {}} = params
const preparedOptions = prependNullOption ? [{ name: 'Not selected', value: null }, ...options] : options
const showEmptyStyle = Object.keys(emptyStyle).length && (!params.value || params.value === NOT_SELECTED)
return (
<select
disabled={params.disabled}
value={params.value}
onChange={e => {
const val = e.target.value
if (!onChange) return
if (typeof val === 'string') {
try {
const parsedVal = JSON.parse(val)
onChange && onChange(parsedVal)
} catch (e) {
onChange && onChange(val)
}
}
}}
className={`${classes.inputSelectForm} ${showEmptyStyle ? classes.emptyInput : null}`}>
{
preparedOptions.map((item, key) => (
<option
key={key}
value={item.value !== undefined ? item.value : item.name}>
{item.name}
</option>
))
}
</select>
)
},
multiselect: (params = {}, error) => (
<MultiSelect
options={params.options}
onChange={params.onChange}
value={params.value} />
),
range: (params = {}, error) => <RangeSelect {...params} />,
checkbox: (params, error) => (
<input
type='checkbox'
{...params}
/>
)
}
const renderInput = (type, params, error, returnEvent) => {
return typeInputs[type] ? typeInputs[type](params, error, returnEvent) : null
}
export const InputForm = ({ icon, type = 'text', params, error, labelText, labelStyle, labelType, customStyle, returnEvent }) => {
return (
<div
className={`${classes.inputContainer} ${type === 'select' || type === 'multiselect' ? classes.inputContainerWithIcon : ''}`}
style={!labelText ? { marginTop: '0', ...customStyle } : customStyle}>
{labelText && (<label className={`${classes[labelType] || classes.inputLabelForm} ${params.disabled ? classes.inputDisabled : ''}`} style={labelStyle} >{labelText}</label>)}
{renderInput(type, params, error, returnEvent)}
{icon && (<i className={`fa fa-${icon} ${classes.inputAddon} ${params.disabled ? classes.inputDisabled : ''}`} />)}
</div>
)
}
export default InputForm
export const NOT_SELECTED = 'not_selected'
export const validateSelected = (property) => {
return property && property !== NOT_SELECTED
}
INPUT
5 types
• text
• select
• multiselect
• range
• checkbox
9 props
params {} 

can match any type
very-very-very-generic
both controlled & uncontrolled
at the same time
I thought GENERIC is a good thing
Mission 2
Access rights
Keep calm,

start small,
refactor in need.
class MemePage extends React.Component {
...
render() {
const { permissions } = this.props
return (
<div>
{
Permissions.canCreateMeme(permissions) ? (
<MemeGeneratorLink />
) : null
}
{/* RENDER MEMES */}
</div>
)
}
}
const mapStateToProps = state => ({ permissions: state.permission })
export default connect(mapStateToProps)(MemePage)
• create meme?
• edit meme?
• delete meme?
• see meme history?
• generate meme for
presentation?
• download meme?
const HasPermission = ({ children, permissions, role}) => {
if (role === 'CREATE_MEME' && Permissions.canCreateMeme(permissions)) {
return children
}
return null
}
const mapStateToProps = state => ({ permissions: state.permission })
export default connect(mapStateToProps)(HasPermission)
class MemesPage extends React.Component {
...
render() {
return (
<div>
<HasPermission role='CREATE_MEME'>
<MemeGeneratorLink />
</HasPermission>
{/* RENDER MEMES */}
</div>
)
}
}
const mapStateToProps = state => ({ permissions: state.permission })
const withPermission = (WrappedComponent, role) => 

connect(mapStateToProps)(({ permissions, ...props }) => {
if (role === 'CREATE_MEME' && Permissions.canCreateMeme(permissions)) {
return <WrappedComponent {...props}/>
}
return null
}
))
export withPermission(MemeGeneratorLink, ‘CREATE_MEME’)
///
class MemesPage extends React.Component {
...
render() {
return (
<div>
<MemeGeneratorLink />
{/* RENDER MEMES */}
</div>
)
}
}
Children 

/ 

HOC 

/

nothing
?
Criteria
• better abstraction
• separation of concerns
• improved maintainability
• better reusability
Context is the king
• project
• stage of project
• component
• developer preferences
• requirements
“We value code that is easy to maintain 

over code that is easy to write”
Nat Pryce, Steve Freeman
Bartek Witczak
@bartekwitczak
bartek@dayone.pl

Mais conteúdo relacionado

Mais procurados

GQL cheat sheet latest
GQL cheat sheet latestGQL cheat sheet latest
GQL cheat sheet latestsones GmbH
 
Building iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" DominoBuilding iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" DominoRob Bontekoe
 
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"Fwdays
 
React.js: Beyond the Browser
React.js: Beyond the BrowserReact.js: Beyond the Browser
React.js: Beyond the Browsergarbles
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code OrganizationRebecca Murphey
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UIRebecca Murphey
 
Patterns / Antipatterns with NoSQL
Patterns / Antipatterns with NoSQLPatterns / Antipatterns with NoSQL
Patterns / Antipatterns with NoSQLLuca Bonmassar
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRick Copeland
 
Drupal Entities - Emerging Patterns of Usage
Drupal Entities - Emerging Patterns of UsageDrupal Entities - Emerging Patterns of Usage
Drupal Entities - Emerging Patterns of UsageRonald Ashri
 
JavaScript Objects and OOP Programming with JavaScript
JavaScript Objects and OOP Programming with JavaScriptJavaScript Objects and OOP Programming with JavaScript
JavaScript Objects and OOP Programming with JavaScriptLaurence Svekis ✔
 
Dig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup CairoDig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup CairoMohamed Mosaad
 
HirshHorn theme: how I created it
HirshHorn theme: how I created itHirshHorn theme: how I created it
HirshHorn theme: how I created itPaul Bearne
 
Windows 8 JavaScript (Wonderland)
Windows 8 JavaScript (Wonderland)Windows 8 JavaScript (Wonderland)
Windows 8 JavaScript (Wonderland)Christopher Bennage
 

Mais procurados (16)

GQL cheat sheet latest
GQL cheat sheet latestGQL cheat sheet latest
GQL cheat sheet latest
 
Building iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" DominoBuilding iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" Domino
 
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
Chris Holland "Leveraging Typed Exceptions for Cleaner Error Handling"
 
React.js: Beyond the Browser
React.js: Beyond the BrowserReact.js: Beyond the Browser
React.js: Beyond the Browser
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
 
Ms Ajax Dom Element Class
Ms Ajax Dom Element ClassMs Ajax Dom Element Class
Ms Ajax Dom Element Class
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UI
 
Patterns / Antipatterns with NoSQL
Patterns / Antipatterns with NoSQLPatterns / Antipatterns with NoSQL
Patterns / Antipatterns with NoSQL
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
 
Drupal Entities - Emerging Patterns of Usage
Drupal Entities - Emerging Patterns of UsageDrupal Entities - Emerging Patterns of Usage
Drupal Entities - Emerging Patterns of Usage
 
JavaScript Objects and OOP Programming with JavaScript
JavaScript Objects and OOP Programming with JavaScriptJavaScript Objects and OOP Programming with JavaScript
JavaScript Objects and OOP Programming with JavaScript
 
BVJS
BVJSBVJS
BVJS
 
Hibernate
HibernateHibernate
Hibernate
 
Dig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup CairoDig Deeper into WordPress - WD Meetup Cairo
Dig Deeper into WordPress - WD Meetup Cairo
 
HirshHorn theme: how I created it
HirshHorn theme: how I created itHirshHorn theme: how I created it
HirshHorn theme: how I created it
 
Windows 8 JavaScript (Wonderland)
Windows 8 JavaScript (Wonderland)Windows 8 JavaScript (Wonderland)
Windows 8 JavaScript (Wonderland)
 

Semelhante a Structuring React.js Components

How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Componentscagataycivici
 
준비하세요 Angular js 2.0
준비하세요 Angular js 2.0준비하세요 Angular js 2.0
준비하세요 Angular js 2.0Jeado Ko
 
Building complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactBuilding complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactJonne Kats
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgetsvelveeta_512
 
Architecture Components In Real Life Season 2
Architecture Components In Real Life Season 2Architecture Components In Real Life Season 2
Architecture Components In Real Life Season 2Somkiat Khitwongwattana
 
React.js: You deserve to know about it
React.js: You deserve to know about itReact.js: You deserve to know about it
React.js: You deserve to know about itAnderson Aguiar
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of usOSCON Byrum
 
How to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQueryHow to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQuerykolkatageeks
 
J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012ghnash
 
Knockoutjs UG meeting presentation
Knockoutjs UG meeting presentationKnockoutjs UG meeting presentation
Knockoutjs UG meeting presentationValdis Iljuconoks
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
jQuery Rescue Adventure
jQuery Rescue AdventurejQuery Rescue Adventure
jQuery Rescue AdventureAllegient
 
Local storage in Web apps
Local storage in Web appsLocal storage in Web apps
Local storage in Web appsIvano Malavolta
 
Web components with java by Haijian Wang
Web components with java by Haijian WangWeb components with java by Haijian Wang
Web components with java by Haijian WangGWTcon
 
SenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark Brocato
SenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark BrocatoSenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark Brocato
SenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark BrocatoSencha
 
Viastudy ef core_cheat_sheet
Viastudy ef core_cheat_sheetViastudy ef core_cheat_sheet
Viastudy ef core_cheat_sheetimdurgesh
 

Semelhante a Structuring React.js Components (20)

How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Components
 
준비하세요 Angular js 2.0
준비하세요 Angular js 2.0준비하세요 Angular js 2.0
준비하세요 Angular js 2.0
 
Building complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and ReactBuilding complex User Interfaces with Sitecore and React
Building complex User Interfaces with Sitecore and React
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
Architecture Components In Real Life Season 2
Architecture Components In Real Life Season 2Architecture Components In Real Life Season 2
Architecture Components In Real Life Season 2
 
React.js: You deserve to know about it
React.js: You deserve to know about itReact.js: You deserve to know about it
React.js: You deserve to know about it
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of us
 
How to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQueryHow to increase Performance of Web Application using JQuery
How to increase Performance of Web Application using JQuery
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012J query b_dotnet_ug_meet_12_may_2012
J query b_dotnet_ug_meet_12_may_2012
 
Redux vs Alt
Redux vs AltRedux vs Alt
Redux vs Alt
 
Knockoutjs UG meeting presentation
Knockoutjs UG meeting presentationKnockoutjs UG meeting presentation
Knockoutjs UG meeting presentation
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
 
jQuery Rescue Adventure
jQuery Rescue AdventurejQuery Rescue Adventure
jQuery Rescue Adventure
 
Local storage in Web apps
Local storage in Web appsLocal storage in Web apps
Local storage in Web apps
 
Web components with java by Haijian Wang
Web components with java by Haijian WangWeb components with java by Haijian Wang
Web components with java by Haijian Wang
 
jQuery
jQueryjQuery
jQuery
 
SenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark Brocato
SenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark BrocatoSenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark Brocato
SenchaCon 2016: Ext JS + React: A Match Made in UX Heaven - Mark Brocato
 
Viastudy ef core_cheat_sheet
Viastudy ef core_cheat_sheetViastudy ef core_cheat_sheet
Viastudy ef core_cheat_sheet
 

Último

H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demoHarshalMandlekar2
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Visualising and forecasting stocks using Dash
Visualising and forecasting stocks using DashVisualising and forecasting stocks using Dash
Visualising and forecasting stocks using Dashnarutouzumaki53779
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityIES VE
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 

Último (20)

H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demo
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Visualising and forecasting stocks using Dash
Visualising and forecasting stocks using DashVisualising and forecasting stocks using Dash
Visualising and forecasting stocks using Dash
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a reality
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 

Structuring React.js Components

  • 2. What do you mean by structuring?
  • 3. What do you mean by structuring? • single component in isolation • relation between components • structuring modules • whole page architecture
  • 4. What do you mean by structuring? • single component in isolation • relation between components • structuring modules • whole page architecture
  • 9. class WelcomeTo4Devs extends React.Component { render () { return 'Welcome to meme generator!' } }
  • 11. const Loading = () => ( <div className='loading'> <i className='icon-refresh spin’/> </div> ) Functional stateless
  • 12. Functional stateless const Loading = ({ text }) => ( <div className='loading'> <i className='icon-refresh spin'/> <div className='loading-text'> {text} </div> </div> )
  • 14. class UsersSelect extends React.Component { constructor(props) { super(props) this.state = { loading: true, options: [] } } componentDidMount() { UsersService.getAll().then(options => { this.setState({ loading: false, options }) }) } render() { const { selected, onSelect } = this.props const { loading, options } = this.state return ( <Select value={selected} isLoading={loading} options={options} onChange={onSelect} /> ) } }
  • 15. Container & Presentational Smart & Dumb View & Controller
  • 16. Container & Presentational Props State Lifecycle event ContextRender Props State Lifecycle event ContextRender
  • 17. const UsersSelect = ({ onSelect, options, selected, }) => ( <Select value={selected} options={options} onChange={onSelect} /> ) Presentational const mapStateToProps = state => ({ options: state.users.options, selected: state.users.selected, }) const mapDispatchToProps = dispatch => ({ onSelect: options => dispatch({ type: 'SELECT_USER', options, }) }) const Users = connect (mapStateToProps, mapDispatchToProps) (UsersSelect) Container
  • 19. const withLoading = WrappedComponent => ({ loading, ...props }) => { if ( loading ) { return <div>Keep calm and eat broccoli ...</div> } else { return <WrappedComponent {...props} /> } }
  • 22. changing one component does not imply changes in other Loose coupling Tight coupling changing one component implies changes in other
  • 24. single responsibility High cohesion Low cohesion multiple responsibilities
  • 27. Challenge: Create meme details panel { name: 'Challenge accepted', file: 'challenge-accepted.jpg', date: '2018-04-05T11:15:30', creator: 'Jim Beam’, }
  • 28. const MemeDetails = ({ meme }) => ( <div className='meme-details'> <div>Name: <span>{meme.name}</span></div> <div>File: <span>{meme.file}</span></div> <div>Date: <span>{format(meme.date, 'YYYY-MM-DD')}</span></div> <div>Creator: <span>{meme.creator}</span></div> </div> ) John ... <MemeDetails meme={meme} /> ...
  • 29. Mike const MemeDetails = ({ name, file, date, creator }) => ( <div className='meme-details'> <div>Name: <span>{name}</span></div> <div>File: <span>{file}</span></div> <div>Date: <span>{date}</span></div> <div>Creator: <span>{creator}</span></div> </div> ) ... <MemeDetails name={meme.name} file={meme.file} date={format(meme.date, ‘YYYY-MM-DD’)} creator={meme.creator} /> ...
  • 30. Tom const MemeDetails = ({ fields }) => ( <div className="meme-details"> {fields.map(f => ( <div> {f.label}: <span>{f.value}</span> </div> ))} </div> ) ... <MemeDetails fields={[ {label: 'Name', value: meme.name}, {label: 'File', value: meme.file}, {label: 'Date', value: format(meme.date, 'YYYY-MM-DD')}, {label: 'Creator', value: meme.creator}, ]} /> ...
  • 32. What about future changes? • changing model schema
  • 33. const MemeDetails = ({ meme }) => ( <div className='meme-details'> <div>Name: <span>{meme.name}</span></div> <div>File: <span>{meme.file}</span></div> <div>Format: <span>{meme.format}</span></div> <div>Date: <span>{format(meme.date, 'YYYY-MM-DD')}</span></div> <div>Creator: <span>{meme.creator}</span></div> </div> ) John ... <MemeDetails meme={meme} /> ...
  • 34. Mike const MemeDetails = ({ name, file, format, date, creator }) => ( <div className='meme-details'> <div>Name: <span>{name}</span></div> <div>File: <span>{file}</span></div> <div>Format: <span>{format}</span></div> <div>Date: <span>{date}</span></div> <div>Creator: <span>{creator}</span></div> </div> ) ... <MemeDetails name={meme.name} file={meme.file} format={meme.format} date={format(meme.date, ‘YYYY-MM-DD’)} creator={meme.creator} /> ...
  • 35. Tom const MemeDetails = ({ fields }) => ( <div className="meme-details"> {fields.map(f => ( <div> {o.label}: <span>{o.value}</span> </div> ))} </div> ) ... <MemeDetails fields={[ {label: 'Name', value: meme.name}, {label: 'File', value: meme.file}, {label: 'Format', value: meme.format}, {label: 'Date', value: format(meme.date, 'YYYY-MM-DD')}, {label: 'Creator', value: meme.creator}, ]} /> ...
  • 36. What about future changes? • changing model schema • formatting
  • 37. const MemeDetails = ({ meme, dateFormat }) => ( <div className='meme-details'> <div>Name: <span>{meme.name}</span></div> <div>File: <span>{meme.file}</span></div> <div>Date: <span>{format(meme.date, dateFormat)}</span></div> <div>Creator: <span>{meme.creator}</span></div> </div> ) John ... <MemeDetails meme={meme} meme=‘YYYY-MM-DD H:mm’/> ...
  • 38. Mike const MemeDetails = ({ name, file, date, creator }) => ( <div className='meme-details'> <div>Name: <span>{name}</span></div> <div>File: <span>{file}</span></div> <div>Date: <span>{date}</span></div> <div>Creator: <span>{creator}</span></div> </div> ) ... <MemeDetails name={meme.name} file={meme.file} date={format(meme.date, ‘YYYY-MM-DD H:mm’)} creator={meme.creator} /> ...
  • 39. Tom const MemeDetails = ({ fields }) => ( <div className="meme-details"> {fields.map(f => ( <div> {o.label}: <span>{o.value}</span> </div> ))} </div> ) ... <MemeDetails fields={[ {label: 'Name', value: meme.name}, {label: 'File', value: meme.file}, {label: 'Date', value: format(meme.date, ‘YYYY-MM-DD H:mm’)}, {label: 'Creator', value: meme.creator}, ]} /> ...
  • 41. High cohesion Tightly coupled with model Single place when modifying model FormattingJohn
  • 42. Loosely coupled with model Formatting Low cohesion Many places when modifying model Mike
  • 43. Very abstract Loosely coupled with model Low cohesion Multiple places when modifying model Very abstract Tom
  • 44. And something from 
 real world?
  • 45. const OptionsList = ({ open, width, options = [], onOptionClick, range, height }) => !open || options.length === 0 ? null : ( <div style={{ width: width < 70 ? 70 : width, maxHeight: height }} className={classes2.optionsList}> { options.map(({ name, value, selected }, key) => ( <div key={key} className={classes2.optionsListItem + ' ' + (selected ? classes2.selected : '')} onClick={e => { e.stopPropagation() onOptionClick(value, !selected) }}> {!range && <input type='checkbox' checked={selected} style={{ marginRight: '5px' }} />} {name} </div> )) } </div> ) export class MultiSelect extends Component { state = { optionsListOpen: false } componentDidMount () { const width = ReactDOM.findDOMNode(this).offsetWidth this.setState({ width }) } getInputText = () => { const value = this.props.value || [] return value.length > 0 ? value.sort().join(', ') : 'Not selected' } onOptionClick = (selectedValue, selected) => { const { onChange } = this.props const value = this.props.value || [] onChange && onChange(selected ? [selectedValue, ...value] : value.filter(item => selectedValue !== item) ) } toggleOptionsList = () => { const { optionsListOpen } = this.state this.setState({ optionsListOpen: !optionsListOpen }) } prepareOptions = () => { const { options } = this.props const value = this.props.value || [] const preparedOptions = [...options] value.forEach(selected => { const optionIndex = preparedOptions.findIndex(({ value }) => value === selected) if (optionIndex !== -1) preparedOptions[optionIndex] = { ...preparedOptions[optionIndex], selected: true } }) return preparedOptions } close = () => this.setState({ optionsListOpen: false }) render () { const { optionsListOpen, width } = this.state const { options, inputSize, customStyle = {}, height } = this.props return ( <ClickOutside onClickOutside={this.close}> <div className={classes.inputSelectForm} style={{ position: 'relative', display: 'flex', height: inputSize || '44px', alignItems: 'center', ...customStyle }} onClick={this.toggleOptionsList}> <span style={{ textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', display: 'block', fontSize: '15px' }}>{this.getInputText()}</span> <div style={{ position: 'absolute', right: 0}}> <ChevronDown marginRight='0' /> </div> <OptionsList open={optionsListOpen} width={width} height={height} options={this.prepareOptions()} onOptionClick={this.onOptionClick} /> </div> </ClickOutside> ) } } export class RangeSelect extends Component { state = { optionsListOpen: false, currentlyEditing: 'min' } componentDidMount () { const width = ReactDOM.findDOMNode(this).offsetWidth this.setState({ width }) } getParsedMinMax = () => ({ min: parseInt(this.props.min, 10), max: parseInt(this.props.max, 10) }) getInputText = () => { const { min = 0, max = 0 } = this.getParsedMinMax() if (!min && min !== 0) return '0' return min === max ? max : `${min} - ${max}` } openOptionsList = () => { const { optionsListOpen, currentlyEditing } = this.state this.setState({ optionsListOpen: true }) } prepareOptions = () => { const { options } = this.props const { min, max } = this.getParsedMinMax() return options .map(({ name, value }) => ({ name, value: parseInt(value, 10) })) .map(option => ({ ...option, selected: option.value >= min && option.value <= max })) } onOptionClick = (selectedValue, selected) => { const { onChange } = this.props const { min, max } = this.getParsedMinMax() const { currentlyEditing } = this.state const parsedValue = parseInt(selectedValue, 10) const newMinMax = { min: min.toString(), max: max.toString() } if (currentlyEditing === 'min') { newMinMax.min = parsedValue newMinMax.max = parsedValue this.setState({ currentlyEditing: 'max' }) } else { if (parsedValue < min) { newMinMax.max = min newMinMax.min = parsedValue } else { newMinMax.max = parsedValue } this.setState({ currentlyEditing: 'min' }) } onChange && onChange(newMinMax) } close = () => this.setState({ optionsListOpen: false, currentlyEditing: 'min' }) render () { const { optionsListOpen, width } = this.state const { options, inputSize, customStyle, height } = this.props return ( <ClickOutside onClickOutside={this.close}> <div className={classes.inputSelectForm} style={{ position: 'relative', display: 'flex', height: inputSize || '44px', alignItems: 'center', paddingRight: '0px', ...customStyle }} onClick={this.openOptionsList}> <span style={{ textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap', display: 'block', fontSize: '15px' }}>{this.getInputText()}</span> <div style={{ position: 'absolute', right: 0}}> <ChevronDown marginRight='0' /> </div> <OptionsList range ref={optionsList => this.optionsList = optionsList} open={optionsListOpen} width={width} height={height} options={this.prepareOptions()} onOptionClick={this.onOptionClick} /> </div> </ClickOutside> ) } } const typeInputs = { text: (params, error, returnEvent) => ( <input type='text' {...params} onChange={(e) => { (params && params.onChange) ? returnEvent ? params.onChange(e) : params.onChange(e.target.value) : null }} className={`${classes.inputForm} ${error ? classes.inputError : ''} ${params.disabled ? classes.inputDisabled : ''} ${ (Object.keys(params.emptyStyle || {}).length && (!params.value || params.value === NOT_SELECTED)) ? classes.emptyInput : null }`} /> ), select: (params = {}, error) => { const { options = [], onChange, prependNullOption, emptyStyle = {}} = params const preparedOptions = prependNullOption ? [{ name: 'Not selected', value: null }, ...options] : options const showEmptyStyle = Object.keys(emptyStyle).length && (!params.value || params.value === NOT_SELECTED) return ( <select disabled={params.disabled} value={params.value} onChange={e => { const val = e.target.value if (!onChange) return if (typeof val === 'string') { try { const parsedVal = JSON.parse(val) onChange && onChange(parsedVal) } catch (e) { onChange && onChange(val) } } }} className={`${classes.inputSelectForm} ${showEmptyStyle ? classes.emptyInput : null}`}> { preparedOptions.map((item, key) => ( <option key={key} value={item.value !== undefined ? item.value : item.name}> {item.name} </option> )) } </select> ) }, multiselect: (params = {}, error) => ( <MultiSelect options={params.options} onChange={params.onChange} value={params.value} /> ), range: (params = {}, error) => <RangeSelect {...params} />, checkbox: (params, error) => ( <input type='checkbox' {...params} /> ) } const renderInput = (type, params, error, returnEvent) => { return typeInputs[type] ? typeInputs[type](params, error, returnEvent) : null } export const InputForm = ({ icon, type = 'text', params, error, labelText, labelStyle, labelType, customStyle, returnEvent }) => { return ( <div className={`${classes.inputContainer} ${type === 'select' || type === 'multiselect' ? classes.inputContainerWithIcon : ''}`} style={!labelText ? { marginTop: '0', ...customStyle } : customStyle}> {labelText && (<label className={`${classes[labelType] || classes.inputLabelForm} ${params.disabled ? classes.inputDisabled : ''}`} style={labelStyle} >{labelText}</label>)} {renderInput(type, params, error, returnEvent)} {icon && (<i className={`fa fa-${icon} ${classes.inputAddon} ${params.disabled ? classes.inputDisabled : ''}`} />)} </div> ) } export default InputForm export const NOT_SELECTED = 'not_selected' export const validateSelected = (property) => { return property && property !== NOT_SELECTED }
  • 46. INPUT 5 types • text • select • multiselect • range • checkbox 9 props params {} 
 can match any type very-very-very-generic both controlled & uncontrolled at the same time
  • 47. I thought GENERIC is a good thing
  • 51. class MemePage extends React.Component { ... render() { const { permissions } = this.props return ( <div> { Permissions.canCreateMeme(permissions) ? ( <MemeGeneratorLink /> ) : null } {/* RENDER MEMES */} </div> ) } } const mapStateToProps = state => ({ permissions: state.permission }) export default connect(mapStateToProps)(MemePage)
  • 52. • create meme? • edit meme? • delete meme? • see meme history? • generate meme for presentation? • download meme?
  • 53. const HasPermission = ({ children, permissions, role}) => { if (role === 'CREATE_MEME' && Permissions.canCreateMeme(permissions)) { return children } return null } const mapStateToProps = state => ({ permissions: state.permission }) export default connect(mapStateToProps)(HasPermission)
  • 54. class MemesPage extends React.Component { ... render() { return ( <div> <HasPermission role='CREATE_MEME'> <MemeGeneratorLink /> </HasPermission> {/* RENDER MEMES */} </div> ) } }
  • 55. const mapStateToProps = state => ({ permissions: state.permission }) const withPermission = (WrappedComponent, role) => 
 connect(mapStateToProps)(({ permissions, ...props }) => { if (role === 'CREATE_MEME' && Permissions.canCreateMeme(permissions)) { return <WrappedComponent {...props}/> } return null } ))
  • 56. export withPermission(MemeGeneratorLink, ‘CREATE_MEME’) /// class MemesPage extends React.Component { ... render() { return ( <div> <MemeGeneratorLink /> {/* RENDER MEMES */} </div> ) } }
  • 57. Children 
 / 
 HOC 
 /
 nothing ?
  • 58. Criteria • better abstraction • separation of concerns • improved maintainability • better reusability
  • 59. Context is the king • project • stage of project • component • developer preferences • requirements
  • 60. “We value code that is easy to maintain 
 over code that is easy to write” Nat Pryce, Steve Freeman Bartek Witczak @bartekwitczak bartek@dayone.pl