React in the Real World

Simen Brekken (@simenbrekken)

Daniel Mahal (@danielmahal)

github.com/unfoldoslo

  • React
  • Vårt siste store prosjekt Anton Sport
  • URL, applikasjonsstate og ruting
  • Data flyt og "stores"
  • Interaksjon og "actions"
  • Server-side rendering og isomorfiske applikasjoner
  • Testing med Jest
  • Webpack
  • Animasjon og CSS

Design byrå

Sterkt tekniskt fokus

Praktisk tilnærming, prototyping og eksperimentering

React

Anton Sport

Komplisert Data

Produkt

Farge

Størrelse

Pris

Prismodell

{
    "id": 7,
    "status": "fulfilled",
    "items": [{
        "status": "fulfilled",
        "shippedQuantity": 1,
        "returnedQuantity": 0,
        "id": 11,
        "productName": "Alpine Jacket",
        "brandName": "PUMA",
        "variantName": "Blue",
        "size": "S",
        "ean": 217445280878,
        "quantity": 1,
        "price": 299.0,
        "vat": 59.8,
        "total": 299.0,
        "variant": {
            "id": 1108,
            "ean": 217445280878,
            "name": "Blue",
            "images": [{
                "id": "products/u5k4pz8mhkns2drq6hcz",
                "height": 1710,
                "width": 1080
            }],
            "price": {
                "retail": 2999.0,
                "normal": 2999.0,
                "discounted": 300.0,
                "campaign": null,
                "current": {
                    "type": 2,
                    "price": 300.0,
                    "vat": 60.0
                },
                "vat": 25.0
            },
            "size": "S",
            "extArticleId": null,
            "stock": {
                "available": 0,
                "threshold": 0
            }
        },
        "product": {
            "id": 1051,
            "name": "Alpine Jacket",
            "description": "Intern link: [Merker](/merker)\n\nEkstern link: [Google](http://google.com)",
            "shortDescription": "Denne nye modellen fra Bjørn Dæhlie er laget i et 2-lags elastisk softshellmateriale",
            "brand": {
                "id": 1010,
                "name": "PUMA",
                "logo": {
                    "id": "0",
                    "width": null,
                    "height": null
                }
            },
            "tags": ["AntonSportWebShopAPI.Models.Tag", "AntonSportWebShopAPI.Models.Tag", "AntonSportWebShopAPI.Models.Tag"],
            "packageSize": "Normal",
            "currentPrice": 190.0,
            "version": "AAAAAAAACyo=",
            "createdAt": "2014-11-05T15:34:44.717+00:00",
            "modifiedAt": "2014-11-24T20:44:01.533+00:00",
            "createdBy": "0",
            "modifiedBy": "0"
        }
    }],
    "customer": {
        "name": "S",
        "email": "simen@unfold.no",
        "mobile": null
    },
    "shipping": {
        "address": {
            "street": "Pilestredet Park 20",
            "postalCode": "0176",
            "locality": "OSLO",
            "countryCode": "no",
            "country": "Norway"
        },
        "service": {
            "id": 3,
            "name": "Levert på dør kveld ",
            "description": null,
            "supplier": null,
            "estimatedDelivery": null,
            "price": 119.0,
            "vat": 37.1875
        },
        "tracking": {
            "mobile": null
        }
    },
    "fulfillments": [{
        "id": 10,
        "status": "shipped",
        "service": {
            "id": 1,
            "name": "consignor",
            "trackingCode": null,
            "referenceId": null,
            "trackingUrl": null
        },
        "items": [{
            "id": 11,
            "productName": "Alpine Jacket",
            "brandName": "PUMA",
            "variantName": "Blue",
            "size": "S",
            "ean": 217445280878,
            "quantity": 1,
            "price": 299.0,
            "vat": 59.8,
            "total": 299.0,
            "variant": {
                "id": 1108,
                "ean": 217445280878,
                "name": "Blue",
                "images": [{
                    "id": "products/u5k4pz8mhkns2drq6hcz",
                    "height": 1710,
                    "width": 1080
                }],
                "price": {
                    "retail": 2999.0,
                    "normal": 2999.0,
                    "discounted": 300.0,
                    "campaign": null,
                    "current": {
                        "type": 2,
                        "price": 300.0,
                        "vat": 60.0
                    },
                    "vat": 25.0
                },
                "size": "S",
                "extArticleId": null,
                "stock": {
                    "available": 0,
                    "threshold": 0
                }
            },
            "product": {
                "id": 1051,
                "name": "Alpine Jacket",
                "description": "Intern link: [Merker](/merker)\n\nEkstern link: [Google](http://google.com)",
                "shortDescription": "Denne nye modellen fra Bjørn Dæhlie er laget i et 2-lags elastisk softshellmateriale",
                "brand": {
                    "id": 1010,
                    "name": "PUMA",
                    "logo": {
                        "id": "0",
                        "width": null,
                        "height": null
                    }
                },
                "tags": ["AntonSportWebShopAPI.Models.Tag", "AntonSportWebShopAPI.Models.Tag", "AntonSportWebShopAPI.Models.Tag"],
                "packageSize": "Normal",
                "currentPrice": 190.0,
                "version": "AAAAAAAACyo=",
                "createdAt": "2014-11-05T15:34:44.717+00:00",
                "modifiedAt": "2014-11-24T20:44:01.533+00:00",
                "createdBy": "0",
                "modifiedBy": "0"
            }
        }],
        "message": "Test kommentar"
    }],
    "returns": [],
    "total": 418.0,
    "vat": 96.9875,
    "payment": {
        "partial": null,
        "id": 1,
        "type": 1,
        "price": 0.0,
        "vat": 0.0,
        "name": "Kortbetaling",
        "details": {
            "chargeId": "ch_14yMWNKYEVdyeGxGly0lCqvo"
        },
        "transactions": [{
            "id": 7,
            "type": "Authorize",
            "transactionReference": "ch_14yMWNKYEVdyeGxGly0lCqvo",
            "amount": 418.0
        }]
    },
    "account": null,
    "comments": null,
    "createdAt": "2014-11-13T12:16:19.887+00:00",
    "modifiedAt": "2014-11-17T08:29:14.88+00:00",
    "createdBy": "0",
    "modifiedBy": "0"
}

Organisering

Små komponenter

Komposisjon

Anton består av 300+ individuelle komponenter

Naturlig navngivning

Enkelt å navigere

URL & Applikasjonsstate

Request & Response

Imitasjon av native apps

URL = Applikasjonsstate

Ruting skjema gir oversikt

 
  <Route handler={Application}>
    <Route path="products" handler={Products}>
      <Route path=":product" handler={Product} />
      <DefaultRoute handler={ProductList} />
    </Route>
    <Route path="checkout" handler={Checkout} />
  </Route>

/products/flippin-snowboard

Application

Products

Product

/products
/

Dataflyt

Application

Sidebar

Cart

cartItems
cartItems

Top down

Component

Data (props)

onAddCartItem
onUpdateCartItem
onRemoveCartItem
onAddCartItem
onUpdateCartItem
onRemoveCartItem
onAddCartItem
onUpdateCartItem
onRemoveCartItem
cartItems

Methods (props)

Product

Cart

Category

Stores

Cart

Lese fra Stores

Component

getItems
getTotal
etc.

Skrive til Stores

A

B

C

A

Skrive til Stores

med Actions

Cart

Component

Action

Flux
Reflux

Relay & GraphQL

Server-side rendering

  • Pre-rendre statisk HTML
  • Klienten kobler seg på eksisterende DOM
  • SEO & Tilgjengelighet
  • Vær forsiktig med "window" og "document"
  • Isomorfiske biblioteker (f.eks. superagent)
  • Caching

Testing med Jest

  • Automatisk mocking
  • Testing av moduler i isolasjon
  • Simulering av browser events

Testing med Jest



    var Product = require('../Product')
    var Actions = require('../Actions')
    
    describe('Add to cart button', function() {
      it('should fire an add to cart action when clicked', function() {
        var props = {id: 1}
        var component = ReactTest.renderIntoDocument(Product(props))
        var button = ReactTest.findComponentWithClass(component, 'add-to-cart')
    
        ReactTest.Simulate.click(button)
    
        expect(Actions.addToCart).toBeCalledWith({productId: 1})
      })
    })

Testing av et komponent

Webpack

Bundling av scripts, styles og assets

Splitting og dynamisk lasting av bundles

Multi-device testing

React Hot Module Loading

Transpiling ES6, JSX etc. via Babel

React Hot Loader

Live Demo (#YOLO)

ReactCSSTransitionGroup
ReactTransitionGroup

AnimationMixin

Cheng Lou
react-tween-state, react-state-stream

Animasjon = Pain

React ‹-› CSS ?

Start tidlig med ruting

Hold komponenter små (composition)

Sentraliser datahåndtering

Frakoble brukerinteraksjoner med actions

Gjenbruk server og klient kode, få ytelse gratis!

Fortsatt noen ting som er knotete (animasjoner og CSS)

Oppsummering

Takk!

Simen Brekken (@simenbrekken)

Daniel Mahal (@danielmahal)

github.com/simenbrekken

React in the Real World

By Simen Brekken

React in the Real World

  • 1,040