React is not the easiest development technology to learn.
A big part of the problem is the Hello World example and subsequent guide don’t do a lot to help along folks with a traditional web programming background. The Hello World tutorial can’t run without a giant in-browser IDE, and the successive guides go a little too-deep/too-fast on topics.
Regardless — React is hard to ignore, and most developers working in React don’t need to think too much about it. Their lead developer tells them how to manage props/state and which files to edit, and/or they learn a few boiler-plate copy/pastes and they’re good to go.
I wanted to understand things at more of a fundamental level. After doing my usual research this is the getting started tutorial I would have preferred. This tutorial assumes you have a working copy of Node.js installed and are familiar with basic package management using npm.
A Simple Development Enviornment
Our Build Tools
To start, we’ll need to use npm
(which ships with Node.js) to create a project and then add three packages to that project as development dependencies.
$ mkdir react-hello-web-developer
$ cd react-hello-web-developer
$ npm init -y # create a package.json file
$ npm install @babel/core --save-dev
$ npm install @babel/cli --save-dev
$ npm install @babel/preset-react --save-dev
All three of these packages are part of Babel. Babel is a javascript compiler, (and is sometimes called a transpiler). Babel can take a file written in one language, and compile it into another language. Or, in the more common case, Babel can take a file written in one dialect of javascript and transform it into another dialect of javascript.
The @babel/core
package is the core babel project. Without it, the other parts won’t run. read more.
The @babel/cli
package is a command line interface for babel. It will let us run npx babel
to compile our React source files. read more
The @babel/preset-react
package is a meta-package, and contains other Babel packages. These other packages contain the rules that allow babel to transform “React javascript” into something that will run in a standard javascript engine. read more
Our Source Files
We’ll want to create two source files. One will be the HTML shell that loads our compiled javascript, the other will be the React source file.
Create a new folder named public
and create the following HTML file in that folder
<!-- File: public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="main.js"></script>
</body>
</html>
This HTML file has a single div
with an id
of root
<div id="root"></div>
and then loads in React via the unpkg
CDN.
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="main.js"></script>
You don’t need to load these files from unpkg
— you could host them locally if you want.
You also might have noticed we’re loading a file named main.js
that we haven’t created yet.
<script src="main.js"></script>
Be patient, we’ll get to public/main.js
momentarily.
Our React Source File
Before we can get to public/main.js
, we need to create our React source file. Create a new folder named src
and in that folder create a new file named src/main.js
with the following contents.
// File: src/main.js
ReactDOM.render(
<h1>Hello World</h1>,
document.getElementById('root')
);
This is not the same file as public/main.js
. In fact, it isn’t really javascript code either. This is a React source file, which is a special dialect of javascript that’s not compatible with standard javascript engines. If you tried running this in a browser or via Node.js, you’d end up with an error.
Uncaught SyntaxError: expected expression, got ‘<‘
There’s no quotes around <h1>Hello World</h1>
. This is not a string, it’s something called a JSX component.
In order to run this stock React Hello World program, we’ll need to compile it into regular javascript.
Compiling our Program
To compile this program, we’ll use babel. Run the following program from the command line
$ npx babel --presets=@babel/preset-react src/main.js
ReactDOM.render( /*#__PURE__*/React.createElement("h1", null, "Hello World"), document.getElementById('root'));
The npx
command (distributed with Node.js and npm) will run command line programs that your project’s packages have installed into ./node_modules/.bin/
. i.e. the following two commands are equivalent
$ ./node_modules/.bin/babel --version
7.12.8 (@babel/core 7.12.9)
# npx is actually a little more complex than we desribed above
# but this isn't an npm tutorial. The linked docs have more
# informaion on everything npx can do
# https://github.com/npm/npx#readme
#
$ npx babel --version
7.12.8 (@babel/core 7.12.9)
Babel’s --presets
option tells babel to load the rules for compiling React javascript from the @babel/preset-react
package we installed earlier.
The output
ReactDOM.render( /*#__PURE__*/React.createElement("h1", null, "Hello World"), document.getElementById('root'));
is our compiled program. Congratulations — you just compiled some React code into vanilla javascript!
Let’s compile this program and create a new src/main.js
file in our public
folder. You can do this by copy-pasting the generated code into pubilc/main.js
, or by using a file the redirection operator.
$ npx babel --presets=@babel/preset-react src/main.js > public/main.js
Load public/index.html
in a browser,
$ open public/index.html
and you’ll see your Hello World message bright as day.
Add Components
So that’s Hello World, but it doesn’t really tell us what makes React special.
React is all about rendering components. Components are javascript objects that render other components. When you pass a component to ReactDOM.render
, you’re telling React that you want to render all your components, all its children, (and all it’s children’s children — all the way down) into browser DOM nodes. i.e. that you want to render them as part of the current HTML document.
Let’s rewrite our program to use a javascript component instead of that inline-jsx code we used earlier. Change src/main.js
so it looks like the following
// File: src/main.js
class HelloWorld extends React.Component {
render() {
return <h1>Hello Component</h1>
}
}
ReactDOM.render(
<HelloWorld/>,
document.getElementById('root')
);
Recompile your program
$ npx babel --presets=@babel/preset-react src/main.js > public/main.js
and open your browser. You’ll see the new, Hello Component message.
What we’ve done above is define a new component by defining a new javascript class named HelloWorld
that extends the base React.Component
class.
// File: src/main.js
// React.Component is a base class provided by
// react.development.js -- in a "real" project
// you might need to import this class
//
// Components are usually, but not always, defined via classes.
// Non-class based components are called "funcional" components
// and the open a whole can of worms as far as understanding what the
// heck is going on with react. You can read more about them
// here: https://www.robinwieruch.de/react-function-component
class HelloWorld extends React.Component {
render() {
return <h1>Hello Component</h1>
}
}
Then, we passed that component as the first argument to the ReactDOM.render
method
// File: src/main.js
ReactDOM.render(
<HelloWorld/>,
document.getElementById('root')
);
Notice that using this component means creating an HTML-like tag (<HelloWorld/>
) from its class name. While React will ultimately render components as HTML nodes in the browser’s DOM tree, they are not, themselves, HTML.
Component Trees
Let’s consider a more complicated example. Replace your src/main.js
file with the following
// File: src/main.js
class Address extends React.Component {
render(){
return <div>
<AddressName/> <br/>
<AddressStreet/> <br/>
<AddressLocation/> <br/>
</div>
}
}
class AddressName extends React.Component {
render() {
return <strong>Pulse Storm LLC</strong>
}
}
class AddressStreet extends React.Component {
render() {
return <span>PO BOX 9334</span>
}
}
class AddressLocation extends React.Component {
render() {
return <span>
<AddressCity></AddressCity>,
<AddressRegion></AddressRegion>
<AddressCode></AddressCode>
</span>
}
}
class AddressCity extends React.Component {
render() {
return <span>Portland</span>
}
}
class AddressRegion extends React.Component {
render() {
return <span> OR </span>
}
}
class AddressCode extends React.Component {
render() {
return <span>97207</span>
}
}
ReactDOM.render(
<Address/>,
document.getElementById('root')
);
Here we’re still rendering a single component
// File: src/main.js
ReactDOM.render(
<Address/>,
document.getElementById('root')
);
However, this Address
Component
// File: src/main.js
class Address extends React.Component {
render(){
return <div>
<AddressName/> <br/>
<AddressStreet/> <br/>
<AddressLocation/> <br/>
</div>
}
}
renders an AddressName
, AddressStreet
, and AddressLocation
component. Each of these components also renders further sub-components. Recompile your program, and you should see a mailing address rendered in your browser
This demonstrates how React lets you separate out various components of your interface. This both helps with development (everyone keeps their chocolate and peanut butter separate) and allows folks to create reusable components that can coexist on the same web page/application.
Automatically Updating UI
The other big selling point of React is how it manages a special variable called state
. We’re not going to get too into the difference between props
and state
in React, but lets consider this final program
// File: src/main.js
/**
* A global variable to hold a reference to our top component.
*
* Used for pedagogical purposes -- not something you'd
* do in real life.
*/
let address
/**
* This is an expedient base class created for
* pedagogical purposes. You probably don't want
* to do this. The React core docs frown at
* the existence of getDerivedStateFromProps (yet
* is ships as part of the API?) and our use of it here
* does mean javascript needs to allocate more objects
* than it normally would if we did this "right"
*/
class DontDoThis extends React.Component {
constructor(props) {
super(props)
this.state = Object.assign({}, props)
}
static getDerivedStateFromProps(props, state) {
return Object.assign({}, props)
}
}
class Address extends React.Component {
constructor(props) {
super(props)
address = this
this.state = {
zip:97210,
region:'OR',
street:'PO Box 9334',
name:'Pulse Storm LLC',
city:'Portland'
}
}
render(){
return <div>
<AddressName {...this.state}/> <br/>
<AddressStreet {...this.state}/> <br/>
<AddressLocation {...this.state}/> <br/>
</div>
}
}
class AddressName extends DontDoThis {
render() {
return <strong>{this.state.name}</strong>
}
}
class AddressStreet extends DontDoThis {
render() {
return <span>{this.state.street}</span>
}
}
class AddressLocation extends DontDoThis {
render() {
return <span>
<AddressCity {...this.state}></AddressCity>,
<AddressRegion {...this.state}></AddressRegion>
<AddressCode {...this.state}></AddressCode>
</span>
}
}
class AddressCity extends DontDoThis {
render() {
return <span>{this.state.city}</span>
}
}
class AddressRegion extends DontDoThis {
constructor(props) {
super(props)
this.state = Object.assign({}, props)
}
render() {
return <span> {this.state.region} </span>
}
}
class AddressCode extends DontDoThis {
constructor(props) {
super(props)
this.state = {
zip:props.zip
}
}
render() {
return <span>{this.state.zip}</span>
}
}
ReactDOM.render(
<Address/>,
document.getElementById('root')
);
Replace your src/main.js
with the above program, recompile, and load your program in the browser. You should see the address again
Next, open your browser’s javascript console,
and type the following
// `address` is our to level component obect
// exposed via a global variable
address.setState({
name: 'Magento Inc.',
region: 'CA',
street: '3640 Holdrege Ave',
city:'Los Angeles',
zip:'90016'
})
Notice that immediately after calling this method, the entire component updated to a new address.
You didn’t need to write any new rendering code to update your component — you just told it about your new data, and React took care of re-rendering everything for you.
This is one of React’s super powers. It forces a separation between your data and your markup/design. Beyond being “the right” thing to do, this has a hidden (but intended) side benefit. By getting you out of the business of creating your own DOM elements React’s core code can optimize these often ill-performant operations. The direct creation of DOM nodes is one of a javascript’s biggest performance bottlenecks, particularly in a fully interactive web application delivered via the browser.
Wrap Up
There’s way more to React than this, of course. But at it’s core React is a system for writing HTML-like tags (called JSX). These tags corresponds to javascript objects that share data (called props and state) and have a mechanism for dynamically rendering/updating your UI/UX (usually the browser) whenever that state data is updated.