kitze / mobx-router

A simple router for MobX + React apps
509 stars 66 forks source link

Interceptable Link #21

Open peterbe opened 7 years ago

peterbe commented 7 years ago

I use the Link component and it's awesome. But there's no way I can intercept that click. In particular, depending on some programmatic logic I'd like to do some things in the onClick that may or may not cancel the click. For example:

<Link
    view={views.podcast}
     params={{id: podcast.id, slug: podcast.slug}}
     store={store}

     // pseudo code!
      onClick={e => {
          if (someCondition) {
            store.app.something = 'foo'
            return false
          } 
      }}
     >
   Click Here
</Link>

It's always a bit weird to have multiple onClick events because it's confusing what the order is supposed to be.

Here's a hack I wrote for my own needs. This works well:

export const InterceptableLink = ({
  view,
  className,
  params = {},
  queryParams = {},
  store = {},
  refresh = false,
  style = {},
  children,
  title = children,
  router = store.router,
  onClick = null,   // THIS IS NEW
}) => {
  if (!router) {
    return console.error('The router prop must be defined for a Link component to work!')
  }
  return (<a
      style={style}
      className={className}
      onClick={e => {

        // THESE LINES ARE NEW
        if (onClick && onClick(e) === false) {
          e.preventDefault();
          return;
        }
        // END NEW LINES

        const middleClick = e.which === 2;
        const cmdOrCtrl = (e.metaKey || e.ctrlKey);
        const openinNewTab = middleClick || cmdOrCtrl;
        const shouldNavigateManually = refresh || openinNewTab || cmdOrCtrl;

        if (!shouldNavigateManually) {
          e.preventDefault();
          router.goTo(view, params, store, queryParams);
        }
      }}
      href={view.replaceUrlParams(params, queryParams)}>
      {title}
    </a>
  )
}

Do you think that's a good idea? If so, I can clean it up in the form of a PR.

peterbe commented 7 years ago

By the way, here's why I think it's an awesome idea. I have a tabular page. It does an AJAX query (e.g. /api/items/?page=1). If you click on one of them, it goes to the "perma page" for that item. Then, to get the data it does an AJAX query again (e.g. /api/items?id=1234 and something like...

.then(result => {
    store.app.item = result.items[0]
})

(It also loads other stuff that isn't displayed in the tabular page)

So by intercepting the click, from the tabular page, I can do this:

          <InterceptableLink
            view={views.podcast}
            params={{id: podcast.id, slug: podcast.slug}}
            store={store}
            onClick={e => {
              store.app.podcast = podcast  // <--- here lies the magic!
            }}
          >
            <img src={imageURL} role="presentation" className="rounded"/>
          </InterceptableLink>

That means that when the perma page loads, the data will already have been put in the store and thus it loads instantaneous!

kitze commented 7 years ago

Ah, this looks really interesting. Will take a stab at it soon! 🙌