Getting Started with React + TypeScript
By: Louis Petrik
Tags: Typescript, JavaScript, Reactjs, Programming, Web Development

Getting Started with React + TypeScript

The basic benefits of combining both

Source: the author

In this article, we will go through a few examples of how to use React & TypeScript. We look at props, interfaces, a few hooks, and form handling.

Of course, we have to build a setup first. The easiest way is to use npx.

npm install -g npx npx create-react-app my-app — template typescript cd my-app/

If you already have a React.js project, you can add TypeScript support to it later.

npm install — save typescript @types/node @types/react @types/react-dom @types/jest

Then simply rename each file extension from “js” and “jsx” to “tsx”.

The good news is that TypeScript and JavaScript are very compatible. This means that we can have code in the .tsx files without problems, which is actually nothing else than ordinary javascript.

An example of this in our app.tsx:

function displayCount(userCount : number) : number {

return userCount

} function add(x) {

console.log(x)

} function App() {

return (

<div className="App">

Current Users: {displayCount(24)}

<button onClick={() => add(24)}>Click me</button>

</div>

);

}

The upper function uses types, which is only possible thanks to TypeScript. The add-function would work the same way only in JavaScript. Nevertheless, we can use both together.

You may need to disable the strict mode in your tsconfig.json: "strict": false

Creating the first component

Now we can use TypeScript to write some code for React.js. Let’s create a function-based component.

For this, we can optionally use the following syntax. So we define a component of type React.FC, which stands for Functional Component.

const UserInput : React.FC = () => {

return <input type=”text”/>

}

Thanks to TypeScript, we can now also define which data types we expect for our props.

const UserInput : React.FC<{text: string}> = (props) => {

return <input type=”text” placeholder={props.text}/>

} <UserInput text="Please enter"/>

If we now pass a number as text, TypeScript will report an error — because we expect a string as a prop with the name text.

Using interfaces for props

But we can also implement the whole thing with an interface.

interface Props {

text: string

} const UserInput : React.FC<Props> = (props) => {

return <input type=”text” placeholder={props.text}/>

}

We can also combine interfaces. In this case, we specify that we expect as props the interface car, which consists of brand and speed, both assigned a data type.

interface Car {

brand: string

speed: number

} interface Props {

car: Car

} const CarSeller : React.FC<Props> = (props) => {

return <p>The {props.car.brand} is {props.car.speed} fast </p>

} <CarSeller car={{brand: "Mercedes", speed: 150}}/>

Making props optional

Maybe the speed is not interesting for the potential buyer of the car. In that case, we don’t have to hand it over, and we can mark it as optional in the interface — this is possible with the question mark.

interface Car {

brand: string

speed?: number

}

Using interfaces for our props, we can create a clean and understandable list of which props our component needs and is optional.

To clean up a bit and make the code easier to read, we can, of course, do without the props keyword in TSX:

const CarSeller : React.FC<Props> = ({car}) => {

return <p>The {car.brand} is {car.speed} fast </p>

}

We can also sort the interfaces into their own files. It makes sense to have an interface for each component in the same file, so you can see directly which props are needed.

But for our props interface, we ultimately access the car interface, and if we need it in several other components, we should create a separate file for it.

CarInterface.ts:

export default interface Car {

brand: string

speed?: number

}

Importing it in the App.tsx:

import Car from “./CarInterface”

interface Props {

car: Car

}

Hooks

Using TypeScripts features like Types is again purely optional for Hooks — but it makes sense. But we don’t have to specify a datatype for useState, as in the following example.

function App() {

const [count, changeCount] = useState<number>(0)

return <p>{count}</p>

}

Again, we can use interfaces to define our data types.

interface userInputInterface {

count: number

} function App() {

const [userInput, changeInput] =

useState<userInputInterface({count: 0}) return <p>{userInput.count}</p>

}

Using Refs

To do this, we need to access something you already know from working with TypeScript outside of frameworks like React or Vue. The types HTMLInputElement — since we are now working with refs.

const inputFieldRef = useRef<HTMLInputElement>(null) const getFocus = () => { inputFieldRef.current?.focus() } return <>

<input type=”text” ref={inputFieldRef} />

<button onClick={getFocus}>Click me</button>

</>

We pass null as a parameter to the useRef, since at the time of execution, the input tag is not yet rendered in the DOM. This way, we make sure that the connection starts from our input tag.

Form handling with refs & TypeScript

We just had HTMLInputElement to access the DOM with a ref. Now let’s have a look at TypeScript-React with refs in handling a form.

A shape always has a Submit button, which makes sure that the shape is sent. Without frameworks, there are some attributes for the form, e.g., to which URL the data should be sent and with which method.

But we want to solve all these things manually, with React.js in JavaScript style. Therefore we write a function, which is triggered when the form is sent — and then prevents the standard procedure with preventDefault().

const App : React.FC = () => { const submitHandler = (event: React.FormEvent) => {

event.preventDefault()

} return (

<form onSubmit={submitHandler}>

<input type=”text” />

<input type=”submit” value=”Submit”/>

</form>

)

}

As a parameter for the submitHandler function, we pass the event, which must be of type FormEvent, because it is an action with our form.

const App : React.FC = () => {

const textInputRef = useRef<HTMLInputElement>(null) const submitHandler = (event: React.FormEvent) => {

event.preventDefault()

const enteredText = textInputRef.current!.value

console.log(“submitted:”, enteredText)

} return (

<form onSubmit={submitHandler}>

<input type=”text” ref={textInputRef} />

<input type=”submit” value=”Submit”/>

</form>

)

}

As the default value for useRef, we take again null, as type, we retake the HTMLInputElement.

Our function for handling the submits now accesses the ref. Using the current! syntax, we tell TypeScript that it is okay if the value is null. Finally, no input has been entered yet.

In the actual input field, we just need to make the connection to the ref, and then everything is ready. If we now press submit, the handler function is executed, which gets the user input from the ref — and outputs it to the console.

So we can evaluate a form with TypeScript & React.