Microsoft To-Do

React practices

πŸ‘‹ my name is Alex

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

Maintenance and Change

Explicitness

Passing state

vs.

ICC

ICC

=

Independently

Connected

Components

We connect

every tiny single component

to the store

The art and magic

of state passing

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

Performance

As if no one asks about performance...

Worry not!

State passing

ICC

Now FIGHT!!!

Huge list = 500 tasks

~20 Drag'n'Drop wrappers

1 ICC Task =Β 

~7 Components

~50 properties

~25 handlers

1 prim. Task =Β 

Some context

Β Nested Connectors

~1700 ms 😱

ICC + react-redux < v5.0.0

~600 ms πŸ’ͺ

No Batcher + react-redux < v5.0.0

No Batcher + react-redux v5.0.0beta3

~500 ms πŸ‘Œ

Primitives + react-redux v5.0.0beta3

~450 ms πŸ‘

More components = less overhead

What we have learned?

25 items -> ~50ms,

500 items -> ~300ms

Our DnD implementation

=

Β 50% perf overhead

What we have learned?

Every connect()

adds

~0.1ms to initial rendering time

What we have learned?

State Passing isΒ  ~10% faster in our hot rendering path πŸ”₯but ...

Single file with ~50 selectors and 25 handlers πŸ™€

What we have learned?

Complexity

vs.

a bit of Performance

What we have learned?

Measure in your own case πŸ”Ž

Use virtualized lists πŸ“œ

Do not create custom Batchers πŸ’©

Compose behaviours with React components mindfully

How toΒ ICC

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)

Using ICC

import AddTask from 'components/AddTask'

// Component stuff ...
render() {
  return (
    <AddTask intentOrigin={ intentOrigin } />
  )
}

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)

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

What's next?

Relationships

Transactions

More structure

State machines?

Going deeper...

Basics

πŸ“œ Data Domain

Advanced

πŸ“œ CQRS Explained

Black Magic

πŸ“œ Statecharts

Thank you!

Questions?

How Microsoft To-Do uses React

By Alexey Migutsky

How Microsoft To-Do uses React

  • 3,742