SlideShare a Scribd company logo
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

More Related Content

What's hot

GQL cheat sheet latest
GQL cheat sheet latestGQL cheat sheet latest
GQL cheat sheet latest
sones 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" Domino
Rob 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 Browser
garbles
 
Functionality Focused Code Organization
Functionality Focused Code OrganizationFunctionality Focused Code Organization
Functionality Focused Code Organization
Rebecca Murphey
 
Ms Ajax Dom Element Class
Ms Ajax Dom Element ClassMs Ajax Dom Element Class
Ms Ajax Dom Element Class
jason hu 金良胡
 
Delivering a Responsive UI
Delivering a Responsive UIDelivering a Responsive UI
Delivering a Responsive UI
Rebecca Murphey
 
Patterns / Antipatterns with NoSQL
Patterns / Antipatterns with NoSQLPatterns / Antipatterns with NoSQL
Patterns / Antipatterns with NoSQL
Luca 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 Ming
Rick Copeland
 
Drupal Entities - Emerging Patterns of Usage
Drupal Entities - Emerging Patterns of UsageDrupal Entities - Emerging Patterns of Usage
Drupal Entities - Emerging Patterns of Usage
Ronald 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 JavaScript
Laurence Svekis ✔
 
BVJS
BVJSBVJS
Hibernate
HibernateHibernate
Hibernate
Leonardo Passos
 
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
Mohamed Mosaad
 
HirshHorn theme: how I created it
HirshHorn theme: how I created itHirshHorn theme: how I created it
HirshHorn theme: how I created it
Paul Bearne
 
Windows 8 JavaScript (Wonderland)
Windows 8 JavaScript (Wonderland)Windows 8 JavaScript (Wonderland)
Windows 8 JavaScript (Wonderland)
Christopher Bennage
 

What's hot (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)
 

Similar to 4Developers 2018: Structuring React components (Bartłomiej Witczak)

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
cagataycivici
 
준비하세요 Angular js 2.0
준비하세요 Angular js 2.0준비하세요 Angular js 2.0
준비하세요 Angular js 2.0
Jeado 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 React
Jonne 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 Widgets
velveeta_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 2
Somkiat 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 it
Anderson 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 us
OSCON 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 JQuery
kolkatageeks
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
Ryunosuke SATO
 
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
ghnash
 
Redux vs Alt
Redux vs AltRedux vs Alt
Redux vs Alt
Uldis Sturms
 
Knockoutjs UG meeting presentation
Knockoutjs UG meeting presentationKnockoutjs UG meeting presentation
Knockoutjs UG meeting presentation
Valdis Iljuconoks
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
Nick Lee
 
Separation of concerns - DPC12
Separation of concerns - DPC12Separation of concerns - DPC12
Separation of concerns - DPC12
Stephan Hochdörfer
 
jQuery Rescue Adventure
jQuery Rescue AdventurejQuery Rescue Adventure
jQuery Rescue Adventure
Allegient
 
Local storage in Web apps
Local storage in Web appsLocal storage in Web apps
Local storage in Web apps
Ivano 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 Wang
GWTcon
 
jQuery
jQueryjQuery
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
Sencha
 
Viastudy ef core_cheat_sheet
Viastudy ef core_cheat_sheetViastudy ef core_cheat_sheet
Viastudy ef core_cheat_sheet
imdurgesh
 

Similar to 4Developers 2018: Structuring React components (Bartłomiej Witczak) (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
 

Recently uploaded

Demystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through StorytellingDemystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through Storytelling
Enterprise Knowledge
 
GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...
GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...
GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...
GlobalLogic Ukraine
 
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeckPoznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
FilipTomaszewski5
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
UiPathCommunity
 
Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!
Tobias Schneck
 
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham HillinQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
LizaNolte
 
Getting the Most Out of ScyllaDB Monitoring: ShareChat's Tips
Getting the Most Out of ScyllaDB Monitoring: ShareChat's TipsGetting the Most Out of ScyllaDB Monitoring: ShareChat's Tips
Getting the Most Out of ScyllaDB Monitoring: ShareChat's Tips
ScyllaDB
 
Dandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity serverDandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity server
Antonios Katsarakis
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Neo4j
 
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
Jason Yip
 
Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
Neo4j
 
The Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptxThe Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptx
operationspcvita
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
Miro Wengner
 
ScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking ReplicationScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking Replication
ScyllaDB
 
What is an RPA CoE? Session 2 – CoE Roles
What is an RPA CoE?  Session 2 – CoE RolesWhat is an RPA CoE?  Session 2 – CoE Roles
What is an RPA CoE? Session 2 – CoE Roles
DianaGray10
 
LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...
LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...
LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...
DanBrown980551
 
From Natural Language to Structured Solr Queries using LLMs
From Natural Language to Structured Solr Queries using LLMsFrom Natural Language to Structured Solr Queries using LLMs
From Natural Language to Structured Solr Queries using LLMs
Sease
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
Neo4j
 
Y-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PPY-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PP
c5vrf27qcz
 

Recently uploaded (20)

Demystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through StorytellingDemystifying Knowledge Management through Storytelling
Demystifying Knowledge Management through Storytelling
 
GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...
GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...
GlobalLogic Java Community Webinar #18 “How to Improve Web Application Perfor...
 
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeckPoznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
Poznań ACE event - 19.06.2024 Team 24 Wrapup slidedeck
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
 
Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!Containers & AI - Beauty and the Beast!?!
Containers & AI - Beauty and the Beast!?!
 
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham HillinQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
 
Getting the Most Out of ScyllaDB Monitoring: ShareChat's Tips
Getting the Most Out of ScyllaDB Monitoring: ShareChat's TipsGetting the Most Out of ScyllaDB Monitoring: ShareChat's Tips
Getting the Most Out of ScyllaDB Monitoring: ShareChat's Tips
 
Dandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity serverDandelion Hashtable: beyond billion requests per second on a commodity server
Dandelion Hashtable: beyond billion requests per second on a commodity server
 
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid ResearchHarnessing the Power of NLP and Knowledge Graphs for Opioid Research
Harnessing the Power of NLP and Knowledge Graphs for Opioid Research
 
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
[OReilly Superstream] Occupy the Space: A grassroots guide to engineering (an...
 
Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024Northern Engraving | Nameplate Manufacturing Process - 2024
Northern Engraving | Nameplate Manufacturing Process - 2024
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
 
The Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptxThe Microsoft 365 Migration Tutorial For Beginner.pptx
The Microsoft 365 Migration Tutorial For Beginner.pptx
 
JavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green MasterplanJavaLand 2024: Application Development Green Masterplan
JavaLand 2024: Application Development Green Masterplan
 
ScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking ReplicationScyllaDB Tablets: Rethinking Replication
ScyllaDB Tablets: Rethinking Replication
 
What is an RPA CoE? Session 2 – CoE Roles
What is an RPA CoE?  Session 2 – CoE RolesWhat is an RPA CoE?  Session 2 – CoE Roles
What is an RPA CoE? Session 2 – CoE Roles
 
LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...
LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...
LF Energy Webinar: Carbon Data Specifications: Mechanisms to Improve Data Acc...
 
From Natural Language to Structured Solr Queries using LLMs
From Natural Language to Structured Solr Queries using LLMsFrom Natural Language to Structured Solr Queries using LLMs
From Natural Language to Structured Solr Queries using LLMs
 
Leveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and StandardsLeveraging the Graph for Clinical Trials and Standards
Leveraging the Graph for Clinical Trials and Standards
 
Y-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PPY-Combinator seed pitch deck template PP
Y-Combinator seed pitch deck template PP
 

4Developers 2018: Structuring React components (Bartłomiej Witczak)

  • 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.
  • 48. I thought GENERIC is a good thing
  • 52. 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)
  • 53. • create meme? • edit meme? • delete meme? • see meme history? • generate meme for presentation? • download meme?
  • 54. 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)
  • 55. class MemesPage extends React.Component { ... render() { return ( <div> <HasPermission role='CREATE_MEME'> <MemeGeneratorLink /> </HasPermission> {/* RENDER MEMES */} </div> ) } }
  • 56. 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 } ))
  • 57. export withPermission(MemeGeneratorLink, ‘CREATE_MEME’) /// class MemesPage extends React.Component { ... render() { return ( <div> <MemeGeneratorLink /> {/* RENDER MEMES */} </div> ) } }
  • 58. Children 
 / 
 HOC 
 /
 nothing ?
  • 59. Criteria • better abstraction • separation of concerns • improved maintainability • better reusability
  • 60. Context is the king • project • stage of project • component • developer preferences • requirements
  • 61. “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