A Fictional Account of How I Invented React: A Simplified Perspective of How React Works
Simple Thread
Posted on February 19, 2019
This past year I decided to catch up on the latest in JavaScript frameworks and learn React. Actually, it was it bit more than just learning React as I also was eager to pick up ES6 and Node. Anyway, after experiencing the complexities of Angular, Backbone, Ember, and Knockout, I found React’s clarity amazing. That said, I did, however, think that the couple books and dozens of articles I read overly complicated the simplicity of React.
I adopted a technology comprehension strategy from a mentor, Eduardo Ross (VP of Technology at ASNA), who said: “I consider how I might have coded it. And, if it’s well designed, I’m usually right.” Anyway, as I began to read more about the intricacies of React that were developed to handle edge cases my mind began to create a simplified view of how React works. I began to ignore these added features and instead develop a simplified perspective of why React was created and how it was implemented.
The following is a work of fiction but it is based on the elegance of the React framework. The story is false but the method names and behaviors are real — names of the innocent were not changed to protect them.
One Language and No Salad Dressing
I’ve been at this Web thing since before Perl scripts, PHP, and the Java Servlet API. I’ve been an author and trainer and, I’ll tell you, this mix-and-match of HTML and INSERT_LANGUAGE_HERE in Java Server Pages (JSP) or Groovy Server Pages (GSP) or Embedded RuBy (ERB) was madness. Then LiveScript came out (sorry, JavaScript) and things got yet more complicated. Using multiple languages in one file is like mixing oil and vinegar — in that they don’t mix.
Actually, that’s salad dressing, but, to make it palatable, you have to add zest and vigorously shake. It’s like your web code needs a comment: Shake brain vigorously before trying to maintain.
You can try this code on Codepen.io. Codepen manages the loading of the required React libraries. ReactDOM
, in the above code, makes the Header 1 element render into a DOM node with the ID of root
. Your React apps might have a minimal HTML page as follows:
<div id="root"></div>
But you could use React on any HTML. The <h1>
contained within the element
variable above looks like HTML. It is not. It is a JavaScript function called h1
, implemented with the React libraries. In fact, during my delusions of creating React, I wrote JavaScript implementations for all the HTML tags. Like I said: no HTML, only JavaScript. (If you are still in ES5-land seeing ES6 transpiled to ES5 might be interesting)
The trivial example above shows the simplicity of React but, it really is not yet very useful. The pages of our applications need to be dynamic.
How might I pass data to one of these JSX components? Following our conceptual mapping of HTML syntax to JavaScript, we could use attributes. The image (img
) tag, for example, has, besides the ever present id
attribute, src
, alt
, height
, and width
. But, for our custom components, we will be creating our own non-HTML JSX functions and we can use any set of attributive name-value pairs. Consider the following:
The above code defines a custom JSX element called Welcome
. The usage of the Welcome JSX element is in the first argument of the call to ReactDOM.render
specifies two attributes: name
and onClick
. Name is set to simple text but onClick is an inline definition of an ES6 fat arrow function. So, using any name-value pairs we can pass any kind of data including function implementations.
I said name-value pairs but, actually, all the attributes with their values are passed as a JavaScript object. So, it’s more correct to say key-value pair.
If you try the Welcome code on codepen you’ll see the value passed via the name key is used in the “Hello from” string. Furthermore, if you click on the text, you’ll see the following in the console log:
click [object HTMLDivElement] div-id
Understand that the function executed from within the Welcome JSX function. The above code passed a JavaScript ES6 anonymous function inline with the attribute name
of onClick
. After all, JavaScript functions, as first class objects or citizens, can be passed around via arguments.
I’m sure you know all about JavaScript handlers for HTML events, such as those listed at w3schools. Various DOM elements generate these events. Before React, we set onclick
attributes directly in HTML source code to specify JS event handlers. This gave way to unobtrusive JavaScript and the binding of element events of DOM nodes in separate scripts using jQuery or addEventListener
. With React you specify the event in either inline JS code or pass a reference to a function.
Note: I did have one problem with the naming of the attribute: I found I had to use mixed-case for the handler to avoid confusion. In fact, stick with camelCase for all your JSX attributes. (Check out the React docs for DOM elements)
My use of the term, attribute, is no longer broad enough to describe key-value pairs passed to React JavaScript functions. Let’s call them properties, or just props
. Properties are the magic that turns components into dynamic elements — they allow the embedding, nesting, and reuse of components.
JavaScript eXpressions
JSX statements are expressions. Their name and properties describe what behaviors their JavaScript implementations provide. Earlier I said JSX stood for JavaScript XHTML but JavaScript eXpression is more articulate.
As React is all JavaScript, your custom React component can contain other custom JSX components (as well as the standard HTML components like h1
, p
, ul
, li
, etc.).
To keep functional React components in the pure function category, their props
parameter should not be modified inside the component. Or, in functional programming terms, they are considered to be immutable objects. You can always re-invoke the function with a new props object, But that gets tedious.
Class
Our complex business requirements necessitate the maintenance of application state. In object oriented programming a class is a template used to instantiate objects that contain state and methods/functions that manipulate that state. So, begrudgingly, in my invention of React, I’m going to need classes. When the state of an object instance of a React class changes, React will automatically update the browser’s DOM. To be very specific about the state that a React class is prepared to react to, those values must be contained within an instance property called, well… state.
Codepen.io hosts a simple example of a React component. The following shows the implementation of the constructor function for the component:
JustMe React application in Codepen.io
state
contains mental
state and state locality
. (Your applications may be a bit more complex.) State
is a JavaScript object that can be as complex as you’d like. What I envisioned was for React to respond to changes in the state
object by turning on the properties watering hose and dowsing its nested JSX with the updated values. React would then update the DOM to reflect the values in the modified state
. To manage the massive process of updating the DOM with state
changes, I minimized it by requiring changes to state
to be done in a function called setState
. After calls to the setState
method complete, the DOM refresh runs.
Consider coding state management yourself…. You have to implement some implementation of the Observable pattern. Just pondering on it now, brings back of painful memories working with updates to Angular 1’s $scope.
The handleStateChange method below embeds a call to setState
change. I’ll explain in a bit why the state
key is defined as a computed property. For now, suffice to say that, after setState
runs, all nested JSX that display values for mental or locality will be refreshed.
Cascading Properties
The render
method of your React class is where you tell React what JSX is to be, well, rendered as DOM. All the JSX functions therein are invoked passing, not just the key-value pairs of attributes in the props
variable. but optionally also the state
.
The codepen was created to show the flow of state through <JustMe>
, <LevelTwo>
, and <LevelThree>
. The first line of the return
clause of the two Level functions does a console.log
of the passed properties:
console.log('LevelTwo props: %O', props)
The result of which, when the page loads, is:
▾ "LevelTwo props: %O" Object {
locality: "VA",
mental: "hanging on",
name: "LevelTwo",
▸ onClick: function click(e) {↔}
▸ onStateChange: function (e) {↔
}
Notice the state
values of mental
and locality
(that were initially set in JustMe’s constructor) are passed. When these state
values change in their parent component (JustMe
), those props
values are passed down to children and trigger a re-render of descendant components that use them (LevelTwo
and LevelThree
.) So in addition to cascading properties, you also get cascading updates and selective re-rendering of stuff that’s changed. Pretty cool. As I said, properties are the magic that enables the propagation of values to nested components.
Lifting State Up
LevelThree
has inputs for the mental
and locality
values. That component is, well…, three levels down from JustMe
. I explained how the values cascaded down the nested component tree, but how is the state
object — maintained by the root component JustMe
— going to be updated?
Property values cascade down but state updates are lifted up!
JustMe
passes its handleStateChange
method to LevelOne
with:
onStateChange={this.handleStateChange}
I could have used any object key to hold a reference to handleStateChange
, including handleStateChange but I used onStateChange
to show you can use whatever name you prefer for your property’s object keys.
LevelTwo
then did the same and passed everything to LevelThree
. But, rather than explicitly specifying the key-value pairs as JustMe
did when it called LevelThree
, in LevelThree
I got lazy and passed the full properties object:
Actually, it used ES6’s spread operator (...props
) to create a new object. Remember: The key-value pairs specified in a JSX are passed as a JavaScript object to the function that implements the JSX.
At any rate, the LevelThree
method has a reference to JustMe’s handleStateChange
function. So, when the user modifies the mental
or locality
input fields, their DOM’s onchange
event is handled by the passed properties onStateChange
reference. Which, we know, came originally from JustMe
. So state changes are “Lifted Up” from the following LevelThree
input fields to LevelTwo
back up to JustMe
:
When It’s Time To StageChange (You’ve Got to Rearrange)
Change is tough, just ask Peter Brady. Now I can explain to you the implementation of handleStateChange
:
It takes one argument, e
, which, when onchange
calls it, will be the event object. The event object has a bunch of properties one of which is target. Target
has a property called name
. Well, here’s the gimmick: LevelTwo
defined the values for the input fields to match the object key names defined in JustMe
. So, when mental
state is changed to “completely nuts” the following code in:
obj[e.target.name] = e.target.value;
Executes essentially as:
state[mental]: “completely nuts”
Where the brackets contain any JavaScript expression that converts to an object property name.
This is really crazy, and I’m so impressed with my React code (which I delusionally created) that updates the DOM: but, as you type “completel” — “y nuts” not yet typed — in the mental input field, you can watch the text displayed in the JSX in JustMe as:
{this.state.mental}
in {this.state.locality}
Changes from:
JustMe’s state:hanging on in VA
To:
JustMe’s state:completel in VA
For added kicks, you can click on the text displayed on the page in the div sections of LevelOne or LevelTwo and the console will display:
“LevelTwo-div clicked” or “LevelThree-div clicked”.
Note: I defined the click
function stand-alone just to be different as it wasn’t necessarily a behavior specific to the JustMe
component.
No Thanks to Dad
Why no inheritance? Inheritance for React provides little. In fact, React is more about nesting JSX (composition) than inheritance. A well accepted principle of OOP is: “Prefer composition over inheritance.” Actually, even though the power of classes is available in React, I recommend you start with functional components and only use class components when state
is required.You will find use of pure functions will make your code easier to understand, test, reuse, and maintain.
Getting Real
OK, OK, I didn’t invent nor have anything to do with the development of React. But you already knew that because you’ve already checked Wikipedia and React’s commit log.
But, as I said, React’s clarity is amazing. It just makes sense — especially when compared to other JavaScript frameworks. React certainly has its own complexities (e.g. callbacks and context) but, for the most part, your code will be more clear without them. That said, you probably should look at React lifecycle events. I’m also quite excited about React Hooks as they let coders package state and side effects in pure functional components.
A few recommendations:
Use codepen.io :Read (and copy) other mini React apps. I want to point out that, often, when React code looks complicated, it is ES6 (or advanced JS) not React that is complicated. I had just picked up ES6 before learning React, which was good because ES6 features like spread, rest, and de-structuring are used heavily in React apps.
Get comfortable with ES6 : I highly recommend the book Simplifying JavaScript.
The reactjs.org Getting Started is great and makes great use of codepen.io. I also got a lot out of the vides at the Material UI getting started.
Hey! I even came up with a clever logo for this new library!
The post A Fictional Account of How I Invented React: A Simplified Perspective of How React Works appeared first on Simple Thread.
Posted on February 19, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
February 19, 2019