Skip to content

Instantly share code, notes, and snippets.

@lxynox
Last active June 22, 2019 08:52
Show Gist options
  • Save lxynox/ff0329529fada271d5a71772d09caa7d to your computer and use it in GitHub Desktop.
Save lxynox/ff0329529fada271d5a71772d09caa7d to your computer and use it in GitHub Desktop.
Simplified client-side router (React)

Navigation source of truth (Browser History)

const createHistory = () => {
  const { history } = window
  const listeners = []
  
  const push = (url, state = {}) => {
    history.pushState(state, null, url)
    listeners.forEach(l => l())
  }
  const replace = (url, state = {}) => {
    history.replaceState(state, null, url)
    listeners.forEach(l => l())
  }
  const listen = (listener) => {
    listeners.push(listener)
    return function unlisten(listener) {
      return listeners.filter(l => l !== listener)
    }
  }
  const getHistory = () => history
  
  return { push, replace, listen, getHistory }
}

Navigation Dispatcher (Link)

const createLink = (history) => {
  class Link extends React.Component {
    constructor() {
      super()
      this.handleClick = this.handleClick.bind(this)
    }
    handleClick(e) {
      const {
        replace,
        to
      } = this.props
      if (replace) {
        history.replace(to)
      } else {
        history.push(to)
      }
      e.preventDefault()
    }
    render() {
      const { replace, to, ...rest } = this.props
      return <a href="" onClick={this.handleClick} {...rest} />
    }
  }
  return Link
}

Navigation Handler (Router & Routes)

const createMatch = (location) => 
  ({ re, component: Comp, ...rest }) => 
    re.test(location.pathname) && <Comp {...rest} />

class Router extends React.Component {
  componentWillMount() {
    this.update = () => this.forceUpdate()
    this.unlisten = history.listen(this.update)
  }
  componentWillUnmount() {
    this.unlisten(this.update)
  }
  renderChildren() {
    return React.createElement(() => this.props.children)
  }
  render() {
    return React.Children.only(this.renderChildren())
  }
}

Initialising

const history = createHistory()
const Link = createLink(history)
const Match = createMatch(location)
export { history, Link, Router, Match }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment