My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more
Router is a View

Router is a View

Jon's photo
Jon
·Feb 27, 2016

I posted idea at discuss.reactjs.org seems no one is very interested. Maybe I should post one here. I say react-router is doing routing wrong since it's way to hold private state, which might be fixed in redux-router. But still, I suppose my idea is valuable.

And I way, router is a View, so it's a component in React. Nothing more than that.

Before digging into that router I want to explain how to create a view like that. Now I'm going to create a own router, it's address bar actually, here's what it look like:

title here

We have a "go back" button, a "forward" button, a "reload" button, and an address bar. Since it's a component, we need to represent that with data. And here's an example of the data in CoffeeScript:

schema.routeTemplate = Immutable.fromJS
  name: 'demo' # give each pattern a name
  data: {} # for path params
  query: {} # for query params

# I left the X, Y, and Z to you to fill
schema.historyList = Immutable.fromJS [
  schema.routeTemplate.set('name', X).set('data', X).set('query', X)
  schema.routeTemplate.set('name', Y).set('data', Y).set('query', Y)
  schema.routeTemplate.set('name', Z).set('data', Z).set('query', Z)
]

In my demo the address is:

http://frp.im/team/a14bb3a?title=hard

I suppose you are familiar with that, it's parsed in Express.js and react-router:

Immutable.fromJS
  name: 'team' # a name, actually with a pattern for it like `/team/:id`
  data:
    id: 'a14bb3a'
  query:
    title: 'hard'

The rest part is now simple since it's React we work with. There's a pointer field somewhere in the store that is pointing to a record in historyList. As someone clicks on go back or forward the pointer updates. Then we get another piece of data to render in the address bar. This step is to render an object into a string. It's easy.

What else I need to implement is the update events, like typing in address directly, like triggering navigation from a click event. The first one is tricky, I have to parse the string by myself and update historyList and my pointer. The second one is easy, just create a object that represents the router and putting it there and it's OK.

So, whether or router is a view, we can implement router with a view in React, or more specifically "implement a address bar in a React component". That's my point!

Now let's go back to browsers. Sad that we have some problems: 1) historyList is put inside the browser we can hardly make it by ourselves, 2) "go back" and "forward" icons are controlled by browser itself, no event.preventDefault is provided. It's actually tricky if I want to turn that into a normal React component like we do with input element.

At last I managed to create something called router-view and it worked well in the past months in production. It's not perfect, but very useful: http://router-view.mvc-works.org/ https://github.com/jianliaoim/router-view

Just like Draft.js recreated rich text editor based on immutable data and unidirectional data flow, I think router should also be recreated based on the principles we recovered along with React. I mean router is a component, it renders the its props like routerTemplate, and callbacks events like onPopstate, and can be used just like normal React components.

As I found in this solution, you don't need to learn new terms like <Route> and <Link>, it's basically emit new data to historyList and can be done with basic React techniques. And nesting router is easy, passing the routerTemplate down to a child component and do routing with a big switch block(in CoffeeScript, might be tricky in JSX without support of everything is an expression).

So that's my idea about router. It's better designed in React's way.

And at last, there are some drawbacks using this router. react-router is tricky but in the meanwhile looks very elegant. It's not elegant anymore using switch block in both parent element and child element. What's more, since state is put in store, which is outside the router component, code like dispatch is required. That means much more code to write. Well, nothing can be perfect.