Od zera do bohatera

wyboista ścieżka mocy - case study o2.pl

Radosław Rosłaniec

Stack

React

alt.js

ES2015 + Babel + Webpack

=

Zespół

React

Grid Container

Tabs

Tab

Boxes Container

Box

Title

componentWillMount i (jego) księga tajemnic


//stuff

componentWillMount() {
  window.addEventListener('focus', this.handleWindowFocus)
}

//stuff
          

//SSR friendly stuff

componentDidMount() {
  window.addEventListener('focus', this.handleWindowFocus)
}

//SSR friendly stuff
          

Okej. Zamontowałeś? Odmontuj!


//stuff

componentDidMount() {
  window.addEventListener('focus', this.handleWindowFocus)
  this.intervalId = setInterval(() => {
    console.log('tick')
  }, 1000)
}

componentWillUnmount() {
  window.removeEventListener('focus', this.handleWindowFocus)
  clearInterval(this.intervalId)
}

//stuff
          

Binding context


class App extends React.Component {
  handleUpdate(event) {
    console.log('update handler')
  }

  render() {
    return (
      <input onChange={this.handleUpdate.bind(this)} />
    )
  }
}
          

class App extends React.Component {
  constructor(props) {
    super(props)
    this.handleUpdate = this.handleUpdate.bind(this)
  }

  handleUpdate(event) {
    console.log('update handler')
  }

  render() {
    return (
      <input onChange={this.handleUpdate} />
    )
  }
}
          

Domyślne wartości


class Title extends React.Component {

  render() {
    return (
      <div>
        {this.props.title || ''}
      </div>
    )
  }
}
Title.displayName = 'Title'
export default Title
          

const emptyTitle = ''
class Title extends React.Component {

  render() {
    return (
      <div>
        {this.props.title || emptyTitle}
      </div>
    )
  }
}
Title.displayName = 'Title'
export default Title
          

class Title extends React.Component {

  render() {
    return (
      <div>
        {this.props.title}
      </div>
    )
  }
}
Title.displayName = 'Title'
Title.defaultProps = {
  title: ''
}
export default Title
          

propTypes


class Article extends React.Component {

  render() {
    const video = this.props.video
    return (
      <div>
        {this.props.title}
        <div>
          {video ? <div>
            <span>{video.title}</span>
            <video>
              <source src={video.url} type="video/mp4">
            </video>
          </div> : null}
        </div>
      </div>
    )
  }
}
Article.displayName = 'Article'
Article.defaultProps = {
  title: ''
}
Article.propTypes = {
  title: React.PropTypes.string,
  video: React.PropTypes.shape({
    url: React.PropTypes.string,
    title: React.PropTypes.string
  })
}
export default Article
          

eslint airbnb

mixins

mixins

komponent wyższego poziomu

alt.js


  your_project
  |--actions/
  |  |--MyActions.js
  |--sources/
  |  |--MyStoreSource.js
  |--stores/
  |  |--MyStore.js
  |--components/
  |  |--MyComponent.jsx
  |--alt.js
  |--app.js
          

Show me the code!


//actions/my-store.js
import { alt } from '../alt.js'

class Actions {
  constructor() {
    this.generateActions('fetchItems', 'setItems', 'fetchFailed')
  }
}

export let MyStoreActions = alt.createActions(Actions)
          

//stores/my-store.js
import { alt } from '../alt.js'

import { MyStoreActions } from '../actions/my-store.js'
import { MyStoreSource } from '../sources/my-store.js'

class Store {
  constructor() {
    this.bindActions(MyStoreActions)
    this.registerAsync(MyStoreSource)
    this.state = {
      items: []
    }
  }
  onFetchItems() {
    this.getInstance().fetchItems()
  }
  onSetItems(items){
    this.setState({ items })
  }
  onFetchFailed() {
    //handle error
  }
}
export const MyStore = alt.createStore(Store, 'MyStore')
          

//sources/my-store.js
import { MyStoreActions } from '../actions/my-store.js'
export const MyStoreSource = {
  fetchItems: {
    remote() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve([1,2,3]);
        }, 1000)
      })
    },
    success:  MyStoreActions.setItems,
    error: MyStoreActions.fetchFailed
  }
}
          

//components/my-component.js
import React from 'react'
import AltContainer from 'alt-container'

import { MyStore } from '../stores/my-store.js'
import { MyStoreActions } from '../actions/my-store.js'

import { Items } from './items.js'

class ItemsContainer extends React.Component {

  componentWillMount() {
    MyStoreActions.fetchItems()
  }

  render() {
    return (
      <AltContainer store={MyStore} actions={MyStoreActions}>
        <Items />
      </AltContainer>
    )
  }
}
ItemsContainer.displayName = 'ItemsContainer'

export default ItemsContainer
          

//components/items.js
import React from 'react'

class Items extends React.Component {

  render() {
    return (
      <div>
        {this.props.MyStore.items.map(item => <div>{item}</div>)}
      </div>
    )
  }
}
Items.displayName = 'Items'

export default Items
          

Silne strony alt.js

- prostota

- zarządzanie stanami

- AltContainer

PODSUMOWANIE

componentWillMount & window addEventListener

removeEventListener
binding context defaultProps propTypes mixins alt === easy flux

dzięki!