bit.ly/mstodo_by
Microsoft To-Do
Microsoft To-Do
React
Redux
Redux-actions
ES6 + TypeScript
eslint plugins
ImmutableJS
...
Just a To-Do list...
Reselect
Design Principles
Data-Driven Design
Immutable Design
Composable abstractions
Domain Driven Design
Prioritized Changeability
Explicitness
Passing state
vs.
ICC
ICC
=
Independently
Connected
Components
State Passing
Model Passing
Primitives Passing
props.completed
props.todo.completed
Model Passing
- Data hierarchy == View hierarchy
- Expensive updates
- Very sensitive to changes
- Where do I add child data?
- Derived data dependencies
Easy vs. Performant
Primitives Passing
- Data hierarchy == View hierarchy
Expensive updates- Sensitive to changes
- Where do I add child data?
Derived data dependencies- Does not extend (too many things)
Performant vs. Extensible
ICC
Data hierarchy == View hierarchyExpensive updatesSensitive to changesWhere do I add child data?Derived data dependenciesDoes not extend (too many things)- Performance?
- SCARY!!!
Extensible vs. ?
Let's split!
Data vs View
Connector + Component = single entity
ICC Data Hierarchy
Not so scary...
ICC View Hierarchy
Not so scary...
What about Performance?
Worry not!
State passing
ICC
Now FIGHT!!!
Rendering: 1717 ms
Total: 1680 ms
ICC + react-redux < v5.0.0
Rendering: 600 ms
Total: 1660 ms
No Batcher + react-redux < v5.0.0
Rendering: 500 ms
Total: 1720 ms
No Batcher + react-redux v5.0.0beta3
- Measure in your own case 🔎
- Use virtualized list 📜
- Do not create custom Batchers 💩
Composing behaviour using React components may not be the best idea...
...but that's a different story!
-
More components = less overhead
25 items -> ~50ms, 500 items -> ~300ms -
Every connect() adds 0.1ms to initial rendering time
-
State Passing is <10% faster in our hot rendering path 🔥but ...
-
Single file with ~50 selectors and 25 handlers 🙀
Complexity
vs.
a bit of Performance
ICC in the wild
1. Connect is partially applied
2. Index exports (component + connect)
3. Specialization goes to subdirectories
ICC in the wild - connect
import { connect } from 'react-redux'
import ...
const mapStateToProps = createSelector(
getAddTask,
...,
(addTask, ...) => ({
title: addTask.title,
...
})
)
const mapActionsToProps = dispatch => ({
onChange: ({ title }) => dispatch(updateAddTask({ title })),
...
})
export default connect(mapStateToProps, mapActionsToProps)
ICC in the wild - Component
import React, { PropTypes } from 'react'
...
export default class AddTask extends React.Component {
// ...
render() {
const { title, onFocus, onPlusClick, isFocused } = this.props
const placeholder = LocalizedString.getString('placeholder_add_task')
return (
<div className="addTask">
<button
className="addTask-icon"
onClick={ onPlusClick }
>
<Icon name={ iconName }/>
</button>
// ...
</div>
)
}
}
ICC in the wild - default/index
import React from 'react'
import AddTask from '../AddTask'
import connect from '../connect'
import '../addTask.styl'
export default connect(AddTask)
Another Example
import React, { PropTypes } from 'react'
import TaskItem from '../TaskItem'
import connect from './connect'
import Checkbox from 'components/Checkbox'
const DefaultTaskItem = props =>
<TaskItem
primaryActionComponent={ <Checkbox id={ props.id } intentOrigin={ props.intentOrigin }/> }
{ ...props }
/>
DefaultTaskItem.propTypes = {
id: PropTypes.string.isRequired,
inToday: PropTypes.bool,
intentOrigin: PropTypes.string
}
export default connect(DefaultTaskItem)
Using ICC
import AddTask from 'components/AddTask'
// Component stuff ...
render() {
return (
<AddTask intentOrigin={ intentOrigin } />
)
}
Composable Store
Normalized State
Directory structure
===
state structure
State is composed of domains
1. No derived data in domains
2. Single Source of Writes
3. Represents Domain Entities
4. Indexed (byId, byOrder)
Example of a domain
1. Derived Data is in selectors
Crucial Conventions
2. Action is handled by only one reducer
3. Actions have domain-specific names
4. Dependent actions
State Machines?
Thank you!
Questions?
Как Microsoft To-Do использует React
By Alexey Migutsky
Как Microsoft To-Do использует React
FDConf 2017
- 3,908