Show a Preloader
Our preloader is a screenshot of the final result. Usually you'd have to wait until the end of the project to make that, but I'll just give you mine. Starting with the preloader makes sense for two reasons:
- It's nicer than looking at a blank screen while data loads
- It's a good sanity check for our environment
We're using a screenshot of the final result because the full dataset takes a few seconds to load, parse, and render. It looks better if visitors see something informative while they wait.
React Suspense is about to make building preloaders a whole lot better. Adapting to the user's network speed, built-in preload functionality, stuff like that. More on that in the chapter on React Suspense and Time Slicing.
Make sure you've installed all dependencies and that npm start
is running.
We're building our preloader in 4 steps:
- Get the image
- Make the
Preloader
component - Update
App
- Load Bootstrap styles in
index.js
Step 1: Get the image
Download my screenshot from Github
and save it in src/images/preloader-screenshot.png
. It goes in the src/images/
directory because we're going to import
it in JavaScript (which makes it part
of our source code), and I like to put non-JavaScript files in assets
. Keeps
the project organized.
Step 2: Preloader component
Our Preloader
is a small component that pretends it's the App
and renders a
static title, description, and a screenshot of the end result. It goes in
src/components/Preloader.js
.
We'll put all of our components in src/components/
.
We start the component off with some imports, an export, and a functional stateless component that returns an empty div element.
// src/components/Preloader.jsimport React from "react"import PreloaderImg from "../images/preloader-screenshot.png"const Preloader = () => <div className="App container"></div>export default Preloader
We import
React (which we need to make JSX syntax work) and the
PreloaderImg
for our image. We can import images because of the Webpack
configuration that comes with create-react-app
. The webpack image loader
returns a URL that we put in the PreloaderImg
constant.
At the bottom, we export default Preloader
so that we can use it in App.js
as import Preloader
. Default exports are great when your file exports a
single object. Named exports when you want to export multiple items. You'll see
that play out in the rest of this project.
The Preloader
function takes no props (because we don't need any) and returns
an empty div
. Let's fill it in.
// src/components/Preloader.jsconst Preloader = () => (<div className="App container"><h1>The average H1B in tech pays $86,164/year</h1><p className="lead">Since 2012 the US tech industry has sponsored 176,075 H1B work visas. Mostof them paid <b>$60,660 to $111,668</b> per year (1 standard deviation).{" "}<span>The best city for an H1B is <b>Kirkland, WA</b> with an averageindividual salary <b>$39,465 above local household median</b>. Medianhousehold salary is a good proxy for cost of living in an area.</span></p><img src={PreloaderImg} style={{ width: "100%" }} alt="Loading preview" /><h2 className="text-center">Loading data ...</h2></div>)
A little cheating with grabbing copy from the future, but that's okay. In real life you'd use some temporary text, then fill it in later.
The code itself looks like HTML. We have the usual tags - h1
, p
, b
,
img
, and h2
. That's what I like about JSX: it's familiar. Even if you don't
know React, you can guess what's going on here.
But look at the img
tag: the src
attribute is dynamic, defined by
PreloaderImg
, and the style
attribute takes an object, not a string. That's
because JSX is more than HTML; it's JavaScript. Think of props as function
arguments βΒ any valid JavaScript fits.
That will be a cornerstone of our project.
Step 3: Update App
We use our new Preloader component in App β src/App.js
. Let's remove the
create-react-app
defaults and import our Preloader
component.
// src/App.jsimport React from "react"// Delete the line(s) between here...import logo from "./logo.svg"import "./App.css"// ...and here.// Insert the line(s) between here...import Preloader from "./components/Preloader"// ...and here.class App extends React.Component {// Delete the line(s) between here...render() {return (<div className="App"><div className="App-header"><img src={logo} className="App-logo" alt="logo" /><h2>Welcome to React</h2></div><p className="App-intro">To get started, edit <code>src/App.js</code> and save to reload.</p></div>)}// ...and here.}export default App
We removed the logo and style imports, added an import for Preloader
, and
gutted the App
class. It's great for a default app, but we don't need that
anymore.
Let's define a default techSalaries
state and render our Preloader
component when there's no data.
// src/App.jsfunction App() {const [techSalaries, setTechSalaries] = useState([])if (techSalaries.length < 1) {return <Preloader />} else {return <div className="App"></div>}}
Nowadays we can define properties directly in the class body without a constructor method. It's not part of the official JavaScript standard yet, but most React codebases use this pattern.
Properties defined this way are bound to each instance of our components so
they have the correct this
value. Late you'll see we can use this shorthand
to neatly define event handlers.
We set techSalaries
to an empty array by default. In render
we use object
destructuring to take techSalaries
out of component state, this.state
, and
check whether it's empty. When techSalaries
is empty our component renders
the preloader, otherwise an empty div.
If your npm start
is running, the preloader should show up on your screen.
Hmm⦠that's not very pretty. Let's fix it.
Step 4: Load Bootstrap styles
We're going to use Bootstrap styles to avoid reinventing the wheel. We're ignoring their JavaScript widgets and the amazing integration built by the react-bootstrap team. Just the stylesheets please.
They'll make our fonts look better, help with layouting, and make buttons look like buttons. We could use styled components, but writing our own styles detracts from this tutorial.
We load stylesheets in src/index.js
.
// src/index.jsimport React from "react"import ReactDOM from "react-dom"import App from "./App"// Insert the line(s) between here...import "bootstrap/dist/css/bootstrap.css"// ...and here.ReactDOM.render(<App />, document.getElementById("root"))
Another benefit of Webpack: import
-ing stylesheets. These imports turn into
<style>
tags with CSS in their body at runtime.
This is also a good opportunity to see how index.js
works to render our app
π
- loads
App
and React - loads styles
- Uses
ReactDOM
to render<App />
into the DOM
That's it.
Your preloader screen should look better now.
If you don't, try comparing your changes to this diff on Github.