Let’s begin learning some React. Here’s what I know about React. It’s one of the big three. Its major corporate sponsor is Facebook. It’s on GitHub at
https://github.com/facebook/react with an MIT license. It uses this terrible syntax called JSX where they intermingle template and scripting into an unreadable mess, and react fans defend that practice like battered wives.
There’s also a hell of a lot I know about it just because it is a modern framework. It’s modular. I’m sure there is some bundler they typically use to distribute—it may or may not be Webpack. It must perforce support the creation of components of some kind and the ability to compose them because that’s how the web works circa 2019. And just like the other frameworks, those components will include a controller and a view that gets automatically updated when the state of the controller changes.
I don’t know anything about how to actually write in React (yet). Also, I’m still not really sure how people make a decision among React, Angular or Vue (or maybe something else). My reasons for preferring Vue over anything else I’ve seen are almost entirely about aesthetic. I do know that Angular presents itself as platform-agnostic so that in principle you could write Angular for use in a browser or on, say, a mobile phone. To me that sounds a car with a built-in toaster. Does React try to do that too? I wonder if it has some magical dependency injection system that makes people cry.
Enough preamble. Let’s make a React widget of some kind. I have a copy of Robin Wieruch’s The Road to Learn React here. That’s a shout-out. I’m just going to glance over it and see how apps are made.
My code for this lesson is available on GitHub in my repo here: https://github.com/xerocross/react-examples. It’s on the branch counter-widget.
OK: there’s a command line tool to help us get started. I’m going to install create-react-app globally. npm install -g create-react-app
. Then create-react-app react-examples
. That’s what I’m going to call this app and repo. Oh thank god. There is a dev server included. Executing yarn start
in the shell starts the dev server. I’m more interested in looking at the files now, so I open the folder in VS Code.
I want to take a look at distribution files, so let’s go ahead and run yarn build
. This should not affect the dev server. It executes “react-scripts build” which means nothing to me yet. In the build folder, I am now presented with a dozen horrifying minified files. I don’t like this. I don’t see any kind of configuration file for React. Looks like if I want all the configuration and fine, granular details at my fingertips I need to run the script yarn eject
. I’m willing to put that off a bit. Let’s look at the source files again.
The file index.js looks like the place where it all beings. I see the line ReactDOM.render(<App />, document.getElementById('root'))
. What that says to me is that <App /> represents the top-level component, “root” is the ID of an html element, and this very script gets executed globally where document is in scope. So this functions replaces the the “root” element with the App component. Clear enough. So let’s look at App.
I open the App.js file, and I’m already bothered. I hope you are following along at home. It brazenly calls itself a .js file, but this is not JavaScript. There’s a big blob of almost-html right in the middle of it. It’s not quoted or anything. It’s like now JavaScript includes a template-literal syntax where you can just directly type html into JavaScript and that blob of html gets parsed into some kind of template object.
I note that what I see is not entirely legal html either. It includes something like an interpolation: <img src={logo} className="App-logo" alt="logo" />
. This src = {logo}
is not html. This is script being included inside the html just as the html itself is being included in a larger script.
Obviously what I’m describing is React’s JSX syntax. I intend to be vocally skeptical of this, but I will try to maintain an open mind about it. Or…I’ll try to try. Maybe I’ll even grow to identify with it over time. Like Stockholm syndrome.
Before diving back into the book, I’m going to see if I can just guess how to operate this and make it do something “reactive”. Trash all the styles. It’s not time for that. Uglier, but simpler, and now that old-fashioned diagram of an atom isn’t spinning anymore. Good. Also trashing all of the template content. I added a variable called count with value 0. I got the view to display it. I added a button that increments the value and logs “increment” to the console. Clicking it works. It logs to the console, increments the value, but it does not update the view. The view still says “Counter: 0”. That is surprising.
The App component this thing shipped with wasn’t interactive in any way, so it’s hard to learn interactivity by inspecting it. I’m looking again at Wieruch’s book and I see something odd. That author used create-react-app also, but it appears that when he did that he was presented with a template written using class notation. What I got is just a function App that returns a blob of JSX. I’m going to translate this into a class syntax now. We note in passing however that the new JavaScript class syntax is just sugar for things we’ve been doing in JavaScript for a long time. It’s not strictly necessary.
I’m looking at examples in Wieruch’s book and right off hand I see three things that are horrifying and unexpected, but they explain why my initial attempt at interactivity did not work.
(1) The first horrifying thing is that he puts the components state explicitly in an object called state. For example, if I’m keeping track of a number, count, then it gets included as part of state like state = {count : 0}
. This is horrifying because the variables defined directly on the class are state. I don’t mean that in a React way. I mean whether React likes it or not, in a class the values of the variables are what the word “state” means. But this isn’t just some quirk of Wieruch’s writing. It appears to be a mandatory part of using the React engine. See also here: https://reactjs.org/docs/state-and-lifecycle.html. Grudgingly, I play along. I have a this.state.count variable. In the doc I linked, they even define state inside the constructor. Gross. Also, to refer to these variables in the template (the blob of JSX right there in the middle of the file), we have to explicitly write this.state.count
. The template is not in any way separated from the script. You can write any kind of JavaScript inside those {}
braces, and the price you pay for that…let’s say “freedom” is that you have to use fully qualified names for everything.
(2) The second horrifying thing is that evidently in vanilla JavaScript class methods do not automatically bind this
to the object—to the instance of the class. I’m going to have to perform that experiment myself to believe it, because I didn’t know that and it’s dumbfounding if true. Oh God it’s true. I added a class method thisTester. All it does is console.log(this)
. Then I added a button to my interface that executes that function. The result? The this keyword evaluates to undefined. Who is responsible for this? How do you add a new class syntax to JavaScript and then make such a blunder as not binding the methods implicitly to their objects? By the way: this is not a React blunder. This falls squarely on JavaScript itself. Here’s a lengthy article on that subject exactly: https://medium.freecodecamp.org/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb. To fix this, we have to explicitly add a line of code in the constructor for every method we declare, specified below.
I’m going to leave my thisTester method as it is to make my point. But the next method I write is increment, and I’ll have to make that one actually work. I’ll go ahead and define the method on the class. Then, in the constructor, I have to add this hideous line of plumbing code: this.increment = this.increment.bind(this)
. Seeing this inside the constructor is like having pipes and wiring and air ducts all running along the outsides of your walls plainly visible and—moreover—if you want to plug in a new appliance you have to run wires directly from some main power supply somewhere. It’s gross.
(3) The third hideous thing. To illustrate this, let me start by just adding the line this.state.count += 1
to my increment method. That’s the actual action I want it to perform. Also a console log so we can verify that the method was called when we click the increment button. With that in place, here’s what we have. Clicking the increment button logs to the console, and I also had it log the new value of this.state.count. The value is getting updated. But the view? Unchanged. The view still says Counter: 0. That’s what I expected from glancing over the book because in React to change the state you have to use some built-in React function called setState. I saved this one for last because it is by far the most horrifying.
About setState—is it 2010? I may as well use jQuery to grab the DOM container where the counter is displayed and imperatively change the value to the new value of this.state.counter. This sucks.
Oy. So now I’m going to use “setState” to change my state because that seems to be what actually fires the view to render again. I’m reading here:
https://reactjs.org/docs/react-component.html?utm_source=caibaojian.com#setstate. Good lord. It appears that to use the setState function one has to rebuild the entire state object from scratch every time. It says “changes should be represented by building a new object based on the input from state
and props
“. Am I supposed to recopy the entire state object by hand in every instance where I write a setState call? One way or another, for React to function properly, it says that setState needs to build a brand new state object without mutating the old one. My code looks like this: this.setState((state, props)=>{return {count : state.count + 1};})
. Here I used an object literal to build an entirely new state object. But imagine if my state had like 50 properties! To make that workable at all, I would have to custom-build a clone function for my state object that builds a new state from scratch based on the old one so I can then alter just the particular property I want and return that new object to React.
If React is so insistent on maintaining a series of immutable states, why doesn’t it handle that under the hood and expose some API that I can use to access that chain of states if it interests me at all to do so? Sure, there are times when having a trail of states behind you is useful—like for undoing previous actions—but React has made that style mandatory AND they have made it so the developer has to actually build that machinery himself every time.
Well, I’ll say this for React: my widget functions now. Also: I hate React so much more now than I did before this lesson. It was kinda like buying a radio with some assembly required. Where’s my soldering iron?