I like the programming language TypeScript, and recently, I’ve been blogging about this language. Earlier, I’ve been blogging about programming in Angular with TypeScript. The time has come, and I’ll start a short series on programming in React.js with TypeScript.
Developing the simplest web page with React
In the real-world apps, a React app is a project with configured dependencies, tools and the build process, but to keep things simple, our first web page will have just a single file index.html, which loads the React library from CDN.
<!DOCTYPE html> <head> <meta charset="utf-8"> <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"> <1> </script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"> <2> </script> </head> <body> <div id="root"></div> <3> <script > const element = React.createElement('h1', <4> null, <5> 'Hello World'); <6> ReactDOM.render(element, document.getElementById('root')); <7> </script> </body> </html>
1. Loading the React package from CDN
2. Loading the ReactDOM package from CDN
3. Adding the div with the id “root”
4. Creating the h1 element using createElement() function
5. We don’t send any data (props) to the h1 element
6. The text of the h1 element
7. Rendering the h1 inside the div
The processes of declaring the page content (React.createElement()) and rendering it to the browser’s DOM (ReactDOM.render()) are decoupled, and the former is supported by the API offered by the React object, while the latter is done by ReactDOM. Accordingly, we loaded these two packages in the heasd section of the page.
In React, all component’s UI elements have to be wrapped in one container, and this web page has a div element with the ID root that serves as such container for the content rendered by React. In the script, we prepare the element to render using React.createElement(), and then invoke ReactDOM.render() that finds the element with the root ID and renders it there.
In Chrome, right-click on Hello World and select the menu Inspect. It’ll open the Dev Tools showing the div with the h1 element inside.
The method createElement() has three arguments: the element, its props (data to be passed to the element) and content. In this case, we didn’t need to provide any props (think attributes) and used null here; I’ll explain what props are for in another blog. The content of h1 is “Hello World”, but it can contain child elements (e.g. ul with nested li elements), which could be created with the nested createElement() calls.
Open the file index.html in your browser, and it’ll render the text Hello World as shown next.
<!DOCTYPE html> <head> <meta charset="utf-8"> <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone/babel.min.js"></script> <!-- 1 --> </head> <body> <div id="root"></div> <script type="text/babel"> // <2> const myElement = <h1>Hello World</h1>; // <3> ReactDOM.render( // <4> myElement, document.getElementById('root') ); console.log(myElement); // <5> </script> </body> </html>
1. Adding Babel from CDN
2. The type of the script is text/babel
3. Assigning a JSX value to a variable
4. Initiating the rendering of myElement to the div
We wouldn’t use CDN for adding Babel to a Node-based project as we did in the code above, but for demo purposes, it suffices. In the Node-based apps, Babel would be installed locally in the project and it would be a part of the build process.
Now that you have a fair understanding of how the very basic pages that use React, let’s switch to Node-based projects and component-based apps and see some tooling that React developers use in the real world.
Generating and running a new React-TypeScript app with create-react-app
If you want to create a React app that includes a transpiler and a bundler, you’d need to add configuration files to your app, and this process is automated by the command-line interface (CLI) called create-react-app (see https://www.npmjs.com/package/create-react-app). This tool generates all required configuration files for Babel and Webpack, so you can concentrate on writing your app instead of wasting time configuring tooling. To install the package create-react-app globally on your computer, run the following command in the Terminal window:
npm install create-react-app -g
create-react-app hello-world --typescript
In a minute or so, all required files will be generated in the directory hello-world and the project dependencies will be installed. In particular, it installs the following React packages:
* react-dom – React package for working with the DOM
* react-scripts – scripts and configurations used by create-react-app
Besides the above packages, the CLI installs Webpack, Babel, TypeScript, their type definition files, and other dependencies. To launch the generated web app, switch to the directory hello-world and run npm start, which in turn runs react-scripts start. Webpack will bundle the app and webpack-dev-server will serve the app on localhost:3000 as shown in the next screenshot.
For bundling, Webpack uses the file webpack.config.js located in the directory node_modules/react-scripts/config.
The good part is that if you generated the project with create-react-app, it’ll recompile the code, rebuild the bundles, and re-render the UI. This functionality is provided by the Webpack Dev Server. The UI of this app tells us to edit the file src/App.tsx (tsx is for TypeScript + JSX), which is the main TypeScript file of the generated app. Open the directory in VS Code, and you’ll see the project files as shown in the next screenshot.
The source code of your app is located ins the src directory, and the public directory is for the assets of your app that shouldn’t be included in the app bundles. For example, your app has thousands of images and needs to dynamically reference their paths, and the directory public is for files that don’t require any processing before deployment.
The file index.html contains an element <div id=”root”>, which serves as a container of the generated React app. You won’t find any <script> tags for loading the React library code there; they’ll be added during the build process when the app’s bundles are ready.
You can run the app and open the Chrome Dev Panel under the Elements tab to see the runtime content of index.html.
The file serviceWorker.ts is generated just in case you want to develop a Progressive Web App (PWA) that can be started offline using cached assets. We are not going to use it in our sample apps.
JSX and TSX
The draft of the JSX specification offers the following definition: “JSX is an XML-like syntax extension to ECMAScript without any defined semantics. It’s NOT intended to be implemented by engines or browsers”.
Every React app has at least one component, which is called a root component, and our generated app has only the root component App. The file with the code of the function App has the name extension .tsx, which tells the TypeScript compiler that it contains JSX. But just having the extension .tsx is not enough for tsc to handle it: you need to enable JSX by adding the jsx compiler option. Open the file tsconfig.json, and you’ll find there the following line:
The jsx option only affects the emit stage – type checking is unaffected. The value preserve tells tsc to copy the JSX portion into the output file changing its extension to .jsx, because there will be another process (e.g. Babel) that will be parsing it. If the value would be react, tsc would turn the JSX tags into React.createElement() invocations as seen in the above screenshot on the right.