in Code

I’ve got a bunch of random ideas sitting around in the old mind palace around ways to improve the Javascript ecosystem and libraries. I figure I should get those out before I either forget them or my brain explodes. So, I present you with a wall of text that is hopefully somewhat useful.

A Different Router API

I’m a contributor on react-router, which is a Javascript library to build routes in React apps. Think “pages” in a single page app (SPA). It’s extremely powerful and handles a lot of things, such as asynchronous data loading and code splitting. It’s really, really good and you should check it out if you haven’t already.

However, I was thinking about a way to simplify its use for most users. Here’s a common pattern you’ll see when using it:


import { Route } from 'react-router'
import App from 'components/App'
import Page1 from 'components/Page1'
import Page2 from 'components/Page2'
import Tab1 from 'components/Tab1'
import Tab2 from 'components/Tab2'
export default const routes = (
<Route path="/" component={App}>
<Route path="page1" component={Page1}>
<Route path="tab1" component={Tab1} />
<Route path="tab2" component={Tab2} />
</Route>
<Route path="page2" component={Page2}>
<Route path="tab1" component={Tab1} />
<Route path="tab2" component={Tab2} />
</Route>
</Route>
)

view raw

routes.js

hosted with ❤ by GitHub

This is just the routes isolated in their own file. It makes it easy to reason about the layout of your app’s pages. But it seems like a lot of typing to set up these special Route components that just link you over to your actual React components. What if we didn’t have to do that?


import App from 'components/App'
import Page1 from 'components/Page1'
import Page2 from 'components/Page2'
import Tab1 from 'components/Tab1'
import Tab2 from 'components/Tab2'
export default const routes = (
<App path="/">
<Page1 path="page1">
<Tab1 path="tab1" />
<Tab2 path="tab2" />
</Page1>
<Page2 path="page2">
<Tab1 path="tab1" />
<Tab2 path="tab2" />
</Page2>
</App>
)

view raw

reroutes.js

hosted with ❤ by GitHub

Neat! How do we do something like that? Instead of wrapping the component inside of a Route, the component is created as a router component explicitly:


import { Component } from 'reroute';
export default class App extends Component {
render() {
return (
<div>{this.props.params.id}</div>
)
}
}

view raw

app.js

hosted with ❤ by GitHub

Why would that be better? One of the more non-obvious features of react-router is that it passes lots of useful props to the Route components, such as this.props.params. However, the component has no guarantees that it’s being used in a route. Practically speaking, that isn’t going to be a problem because you’re in complete control of your code and it would be a weird pattern to expect route props and then not use that component in a route. However, being explicit about your code’s expectations are always a good thing.

In addition, you could potentially access things like params even when your component isn’t being used directly as a route. That information could be passed down via this.context and accessed by any child node under a route.

Of course, this doesn’t take into account more advanced use cases, such as asynchronous routes, but it could be an interesting pattern to explore for simple use cases.

Autoloading Javascript

I’ve written a lot of Rails code. I still do. Ruby is a great language for backend systems and Rails makes it exceedingly simple and terse to whip up a full-featured REST API in no time. I recently tried to do the same in Node and it was like pulling teeth. There was a lot of DIY setup and fiddling just to get things working. Gross. I know things like Adonis exist to make this better, so hopefully its popularity will grow over time.

But one fixable part of the problem was how much space was wasted on explicitly defining imports in every file. Look at the examples above and notice how nearly half the code is imports. This is something Rails solves with a fantastic autoloader so you never have to require a single file in Rails. It’s almost an anti-pattern if you do. Wouldn’t it be great if you could do that in Javascript too? So, the previous routes file might look like this:


export default const routes = (
<App path="/">
<Page1 path="page1">
<Tab1 path="tab1" />
<Tab2 path="tab2" />
</Page1>
<Page2 path="page2">
<Tab1 path="tab1" />
<Tab2 path="tab2" />
</Page2>
</App>
)

A third of the code gone, but even better, we don’t have to edit the code in two places to add another route: once for the import, once for the route itself.

How would one implement something like this? One way might be a plugin to Babel to look up undefined variables and add in import statements to the code dynamically. You might also override on a per-path basis with a .autoload file, which would let the autoloader know to only search in our route components path. In a Redux combineReducers instance, it would have it autoload from the path to your reducer functions.

Webpack Preset

This last one is a little more self-serving. I’ve been (slowly) working on building out more of Webpack Preset. One of the things I don’t like about webpack (and why I always preferred Gulp to Grunt) is its use of a configuration object instead of a configuration API. (Even better, it could offer both!) So, most of my presets end up doing some funky object manipulation and have a lot of redundant code between them.

What I plan on building out in more detail is a configuration API to webpack. Jason Quense has already experimented with this a bit, and I will probably end up poaching bits of it (with credit, of course!). But my thoughts are to make this a separate library so anyone can use it.

One of the most complex parts of it will be allowing us to serialize the constructed config into a webpack.config.js file. How best should you convert a plugin back to a new webpack.optimize.OccurenceOrderPlugin() statement? I’m not 100% sure how, but it could be pretty interesting to find out!

Leave a Reply