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

  1. Data hierarchy == View hierarchy
  2. Expensive updates
  3. Very sensitive to changes
  4. Where do I add child data?
  5. Derived data dependencies

Easy vs. Performant

Primitives Passing

  1. Data hierarchy == View hierarchy
  2. Expensive updates
  3. Sensitive to changes
  4. Where do I add child data?
  5. Derived data dependencies
  6. Does not extend (too many things)

Performant vs. Extensible

ICC

  1. Data hierarchy == View hierarchy
  2. Expensive updates
  3. Sensitive to changes
  4. Where do I add child data?
  5. Derived data dependencies
  6. Does not extend (too many things)
  7. Performance?
  8. 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

  1. Measure in your own case 🔎
  2. Use virtualized list 📜
  3. Do not create custom Batchers 💩

Composing behaviour using React components may not be the best idea...

...but that's a different story!

  1. More components = less overhead
    25 items -> ~50ms, 500 items -> ~300ms

  2. Every connect() adds 0.1ms to initial rendering time

  3. State Passing is  <10% faster in our hot rendering path 🔥but ...

  4. 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