mahaplatform/reframe

View on GitHub
src/components/stack/router.js

Summary

Maintainability
A
50 mins
Test Coverage
import matchPath from 'react-router-dom/matchPath'
import PropTypes from 'prop-types'
import React from 'react'
import Stack from './stack'

class RouterStack extends React.Component {

  static childContextTypes = {
    stack: PropTypes.object
  }

  static propTypes = {
    action: PropTypes.string,
    pathname: PropTypes.string,
    rootPath: PropTypes.string,
    routes: PropTypes.object
  }

  static defaultProps = {
    rootPath: '/'
  }

  state = {
    cards: []
  }

  render() {
    const { cards } = this.state
    return <Stack cards={ cards } />
  }

  componentDidMount() {
    const { pathname, rootPath } = this.props
    if(pathname === rootPath) return
    const card = this._matchRoute(pathname)
    this.setState({
      cards: [ card ]
    })
  }

  componentDidUpdate(prevProps) {
    const { action, pathname } = this.props
    const { mounted } = this.state
    if(prevProps.pathname !== pathname) {
      if(action === 'push') {
        const card = this._matchRoute(pathname)
        this._handlePush(card)
        setTimeout(() => this.setState({ mounted: mounted + 1 }), 50)
      } else if(action === 'pop') {
        this.setState({ mounted: mounted - 1 })
        setTimeout(this._handlePop.bind(this), 50)
      }
    }
  }

  getChildContext() {
    return {
      stack: {
        push: this._handlePush.bind(this),
        pop: this._handlePop.bind(this)
      }
    }
  }

  _handlePush(card) {
    this.setState({
      cards: [
        ...this.state.cards,
        card
      ]
    })
  }

  _handlePop() {
    const cards = this.state.cards.slice(0, -1)
    this.setState({ cards })
  }

  _matchRoute(pathname) {
    const { routes } = this.props
    return Object.keys(routes).reduce((component, path) => {
      if(component) return component
      const matched = matchPath(pathname, { path, exact: true })
      if(!matched) return null
      return {
        pathname,
        component: routes[path],
        props: {
          pathname,
          params: matched.params
        }
      }
    }, null)
  }

}

class RouterStackWrapper extends React.Component {

  static contextTypes = {
    router: PropTypes.object
  }

  static propTypes = {
    children: PropTypes.any,
    routes: PropTypes.object
  }

  render() {
    return (
      <RouterStack { ...this._getRouter() }>
        { this.props.children }
      </RouterStack>
    )
  }

  _getRouter() {
    const { action, pathname } = this._getHistory()
    return {
      ...this.props,
      pathname,
      action: action.toLowerCase()
    }
  }

  _getHistory() {
    const { router } = this.context
    return router.history ? {
      action: router.history.action,
      pathname: router.history.location.pathname
    } : router
  }

}

export default RouterStackWrapper