Book Image

React Application Architecture for Production

By : Alan Alickovic
Book Image

React Application Architecture for Production

By: Alan Alickovic

Overview of this book

Building large-scale applications in production can be overwhelming with the amount of tooling choices and lack of cohesive resources. To address these challenges, this hands-on guide covers best practices and web application development examples to help you build enterprise-ready applications with React in no time. Throughout the book, you’ll work through a real-life practical example that demonstrates all the concepts covered. You’ll learn to build modern frontend applications—built from scratch and ready for production. Starting with an overview of the React ecosystem, the book will guide you in identifying the tools available to solve complex development challenges. You’ll then advance to building APIs, components, and pages to form a complete frontend app. The book will also share best practices for testing, securing, and packaging your app in a structured way before finally deploying your app with scalability in mind. By the end of the book, you’ll be able to efficiently build production-ready applications by following industry practices and expert tips.
Table of Contents (13 chapters)

TypeScript setup overview and usage

JavaScript is a dynamically typed programming language, meaning it doesn’t catch any type errors during build time. That’s where TypeScript comes into play.

TypeScript is a programming language that acts as a superset of JavaScript, which allows us to write JavaScript with some behaviors of a statically typed language. This comes in handy as we can catch many potential bugs before they get into production.

Why TypeScript?

TypeScript is especially useful for large applications built by large teams. Code written in TypeScript is much better documented than code written in vanilla JavaScript. By looking at the type definitions, we can figure out how a piece of code is supposed to work.

Another reason is that TypeScript makes refactoring much easier because most of the issues can be caught before running the application.

TypeScript also helps us utilize our editor’s IntelliSense, which shows us intelligent code completion, hover information, and signature information, which speeds up our productivity.

TypeScript setup

Our project already has TypeScript configured. The TypeScript configuration is defined in the tsconfig.json file at the root of the project. It allows us to configure how strict we want it to be based on our needs:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "src"],
  "exclude": ["node_modules"]
}

We will not dive too deeply into every configuration property since most of the properties have been auto-generated. However, there is one thing that was also provided:

   "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }

This will tell the TypeScript compiler that anything imported via @/* will refer to the src folder.

Previously, we had to perform messy imports, like so:

import { Component } from '../../../components/component'

Now, we can import components like so:

import { Component } from '@/components/component'

No matter how many nested levels we have, we can always import with absolute paths, and we will not be required to change our import statement should we decide to move the consumer file somewhere else.

Basic TypeScript usage

Let’s cover some TypeScript basics so that we are comfortable using it throughout this book.

Primitive types

let numberVar: number;
numberVar = 1 // OK
numberVar = "1" // Error
let stringVar: string;
stringVar = "Hi"; // OK
stringVar = false; // Error
let stringVar: string;
stringVar = "Hi"; // OK
stringVar = false; // Error

As we can see, we are only allowed to assign values with the corresponding type. Assigning to any other type except the any type, which we will cover in a moment, will cause a TypeScript error.

Any

The any type is the loosest type in TypeScript and using it will disable any type checking. We can use it when we want to bypass errors that would usually occur. However, we should only use it as a last resort and try to use other types first:

let anyVar: any;
anyVar = 1; // OK
anyVar = "Hello" // OK
anyVar = true; // OK
numberVar = anyVar; // OK

As we can see, variables with the any type can accept and be assigned to a value of any other type, which makes it very flexible.

Unknown

Sometimes, we can’t know upfront which types we will have. This might happen with some dynamic data where we don’t know its type yet. Here, we can use the unknown type:

let unknownVar: unknown;
unknownVar = 1; // OK
unknownVar = "123" // OK
let unknownVar2: unknown;
unknownVar = unknownVar2; // OK
anyVar = unknownVar2; // OK
numberVar = unknownVar2; // Error
stringVar = unknownVar2; // Error
booleanVar = unknownVar2; // Error

As we can see, we can assign values of any type to the variable with unknown type. However, we can only assign values with type unknown to the variables with any and unknown types.

Arrays

There are two ways to define array types with TypeScript:

type numbers = number[]
type strings = Array<string>

Objects

Object shapes can be defined in two ways:

type Person = {
  name: string;
  age: number;
}
interface Person {
  name: string;
  age: number;
}

The first one is called type alias, while the second is called interface.

There are a few differences between type aliases and interfaces, but we won't get into them right now. For any object shape type we define, we can use type aliases.

Unions

The basic types we just mentioned are great, but sometimes, we want to allow a variable to be one of many types. Let’s look at the following example:

type Content = string | number;
let content: Content;
content = 1 // OK
content = "Hi"; // OK
content = false // Error

As we can see, the content variable can now be either string or number.

We can also add literal types in the union, as shown in the following example:

type Color = "red" | "green" | "blue";
let color: Color;
color = "red" // OK
color = "yellow" // Error

Here, we are defining colors as strings, but we want to add more constraints so that we can only take one of those three colors. If we try to add anything else, TypeScript will warn us with an error.

Intersections

Intersection types allow us to combine the properties of two different objects into a single type. Consider this example:

type Foo = {
  x: string;
  y: number;
}
type Bar = {
  z: boolean;
}
type FooBar = Foo & Bar;

The FooBar type will now contain the x, y, and z properties.

Generics

Generics is a mechanism of creating reusable types by parameterizing them. They can help us reduce code repetition. Consider the following type:

type Foo = {
  x: number;
}

Let’s see what happens if we need the same structure but with x as a string:

type Foo = {
  x: string;
}

Here, we can see that there is some code duplication going on. We can simplify this by making it generic so that it accepts the type as T. This would be assigned as the type of the x property:

type Foo<T> = {
  x: T;
}
let x: Foo<number>;
let y: Foo<string>;

Now, we have a nice way to reuse the structure by passing different types to the generic.

We can also use generics with functions:

function logger<T>(value: T) {
  console.log(value)
}
logger<number>(1) // OK
logger<string>(1); // Error

To try out these snippets and see how different types behave, go to https://www.typescriptlang.org/play, copy the snippets, and play around with the types to see how they work.

TypeScript and React

Every TypeScript file that uses JSX must have the .tsx extension.

Typing React components is very straightforward:

type InfoProps = {
  name: string;
  age: number
};
const Info = (props: InfoProps) => {
  return <div>{props.name}-{props.age}</div>;
};

These examples are pretty trivial. We will see more practical examples in the upcoming chapters when we start building the application. To learn more about TypeScript, it is recommended to check the TypeScript handbook at https://www.typescriptlang.org/docs, which covers all these topics in much more detail.