How I Built My Own “React” in Two Days — and Why I Did It
Let’s talk about reinventing the wheel. A major philosophy I have always believed in is learning as much as you can about the ins and outs of all the tools you use. Peer into the GitHub repos of libraries you use, and try to get a real glimpse at how the gears in the author’s brain are turning.
But most of all, I suggest to anyone who wants a true understanding of the frameworks, libraries and tools they use — build your own. Go ahead, reinvent the wheel.
I hear people talk about reinventing the wheel as if it’s a bad thing, but how else are you supposed to get a real, deep understanding of how these things are built? Building clones of things that are already in existence will help you understand that these frameworks, libraries, programming languages we use everyday are not magic. They have bugs and oddities just like we’ve all written at some point.
When I first started programming, I tried making games. After a while, I realized that I wanted to learn more about how the game engines I used worked internally — how all the interdependent moving parts of the system form a whole. Building my own programming languages made me write more efficient code. These projects may never see the light of day. But building them was invaluable, and shaped the way I program today.
I’ve spent the last few months using React to build a website called BidSquid. During this time, I got to know the React component lifecycle fairly well, and I spent a lot of time writing JSX. As odd as it feels writing HTML-like code directly in your JavaScript, I do really enjoy it. Over this past weekend I decided to have some fun and create my own “HTML”-like language like JSX.
I’ve had some ideas for this language for a few months, but I never really got around to actually building it. My main idea was that: from a higher level, CSS and JavaScript have a similar syntax — curly braces and semicolons. CSS style declarations even sort of look like JSON objects. HTML, being an XML-based markup language, looks completely different, having everything nested within <tags> rather than curly braces. I wanted to make a simple language that would fit in more with CSS and JS, and it would convert directly to HTML. Let me tell about how it went.
To start things out, I wrote a simple lexer. For those that don’t know what that is, in a nutshell, a lexer is something that reads input (like file contents) and outputs _tokens —_A token may be an identifier, a string, a curly brace, semicolon, or basically any other character (or set of characters) that may be in your code. For the most part, lexers tend to be pretty much the same and can be reused from project to project. I had a head start on the lexer, as I’d written them in C++ before, and it was just a matter of rewriting one in JavaScript.
For the next part, I wrote a parser. A parser takes the tokens that were generated by the lexer and makes sense of it all. If we were to think about this in the context of a written language like English, you could define a token as a single word, an apostrophe, an exclamation mark, what have you. The parser’s task is to piece these tokens into a logical format — a sentence.
Unlike lexers, parsers are pretty specific to the language you’re creating. For that reason, I ended up spending more time working on the parser. In the matter of a few hours, I was able to throw together a basic working prototype of what I call blockml — because, well, blocks. And markup language. (You can download blockml with npm)
How it ended up looking
After building this little language, I decided I should make an extension that would allow users to define their own components and write them directly in the markup language just like any other div
. Components should have props, states, nested child components, you name it. I decided to name this project blockml-component. Because well, it’s blockml, with components. (You can download blockml-component on npm)
I started out by extending blockml itself to support custom handlers for specific tags. Assuming you added a custom handler for any div element, blockml would dispatch that handler rather than outputting a <div> in HTML like usual. With these custom handlers, blockml-component is able take control and implement more complex functionality for user-defined components.
User-defined components consist of a render()
method, just like in React. The main difference is that instead of writing JSX elements with <tags>, the render function just needs to return a string containing blockml code, which can then be fed to the lexer and parser. Using ES6’s new template strings (the backtick) you can embed JavaScript code with the built-in ${}
, just like React’s {}
or AngularJS’s {{}}
.
This next part is where things get a little technical. Explaining code over text is always tricky, so if you want to get a better grasp of how this part works, check it out on GitHub.
How blockml-component handles nested child components
When you first define a component, blockml-component does some things to make props and child components work. It takes all the attributes that were written on the element, and adds them to a ‘props’ object. This object is passed to the render
method of our component. It also takes all nested components that were written within the component’s set of curly braces, stashes them in an array called _cachedChildren
for later use, and replaces them with a temporary element called __InnerChildren
. This temporary element has a attribute attached to it called index
which we will use later to point us to the correct object in _cachedChildren
. This way, child components don’t have to get analyzed and parsed more than once, and can simply be spliced in later on.
I have a custom handler set up for any __InnerChildren
elements that are found, which simply extracts the index
attribute, and replaced it with the object stashed away in _cachedChildren
. At this point, everything should be filled out and the child elements should be passed to the nested elements. Everything looks like I would expect, coming from the world of React. As simple as it is right now — it is functional, and it is already at a point where it can be built upon and made into something awesome.
Thanks for sticking through my article and letting me tell you my story of how, and why I built my own “React”. I hope you will consider reinventing the wheel sometime. While it may not be a replacement for the more popular libraries and frameworks, you will learn a whole lot and be a smarter programmer for it.
Links
- Download or try blockml-component (an extension to blockml): https://www.npmjs.com/package/blockml-component
- blockml-component source code on GitHub: https://github.com/ajmd17/blockml-component
- Download or try blockml: https://www.npmjs.com/package/blockml
- blockml source code on GitHub: https://github.com/ajmd17/blockml