react-typescript
https://youtu.be/WlxcujsvcIY?si=3EUEqcPIz_aIxIEQ
TypeScript
//// TYPESCRIPT AUTOMATICALLY ASSIGNS A TYPE WHEN YOU DEFINE A VARIABLE
let variable = "hello"; // it internally assign this variable to type string --> type is string
variable = "hi"; // we can only assign/ reassign string
let age = 18; // type is number
// age="eighteen" // give error when string is assigned , when the type is number
//// EXPLICITLY PROVIDING A TYPE
let ageWithType: number = 22;
// ageWithType ="eighteen"
ageWithType = 18;
//// BASIC TYPES
let testString: string;
testString = "hello";
let testBoolean: boolean;
testBoolean = false;
//// MULTIPLE TYPES (UNION TYPES)
let testStringOrNumber: string | number;
testStringOrNumber = 10;
testStringOrNumber = "10";
// testStringOrNumber = [] // gives error when assign to array type
//// ARRAYS
let names = ["john", "jane", "tom"]; // array of string type
// names.push(3) //gives error when we try to push No to array of string type
names.push("mike");
let numbers = [11, 22, 35]; // array of number type
// numbers.push(true)
numbers.push(92);
let testStringArray: string[];
// testStringArray = [1,2,3]
testStringArray = ["one", "two", "three"];
let testNumberArray: number[];
// testNumberArray = [true, "hi", 23]
testNumberArray = [12, 55, 23];
let testStringOrNumberArray: (string | number)[];
testStringOrNumberArray = [1, "two", 3];
//// OBJECTS
let user = {
username: "john",
age: 22,
isAdmin: false,
};
user.username = "jane";
// user.age = "eighteen"
user.age = 29;
// user.isAdmin = "no"
user.isAdmin = true;
// user.phone = "+12345678"
let userObj: {
username: string;
age: number;
isAdmin: boolean;
};
userObj = {
username: "john",
age: 23,
isAdmin: true,
// phone:"+1234567" //we cant add an extra property
};
//optional property
let userObj2: {
username: string;
age: number;
isAdmin: boolean;
phone?: string; //optional
};
userObj2 = {
username: "jane",
age: 43,
isAdmin: false,
phone: "+1234567",
};
userObj2 = {
username: "jane",
age: 43,
isAdmin: false,
};
//// ANY TYPES ( BE CAREFUL )
let testAny: any;
testAny = 12;
testAny = "Hello";
testAny = true;
testAny = [true];
testAny = {};
let testAnyArray: any[];
testAnyArray = [1, "two", false, []];
//// FUNCTIONS
// void --> cant return
let sayHi = () => {
console.log("Hi, welcome");
};
// sayHi = "hi" // we cant reassign fn to string(any other type)
// return type is string
let funcReturnString = (): string => {
console.log("hi");
return "lama dev";
};
// arg -> no , return -> no
let multiple = (num: number) => {
return num * 2;
};
// arg -> no , return -> no
let multiple2 = (num: number): number => {
return num * 2;
};
// void --> cant return
let multiple3 = (num: number): void => {
num * 2;
//Do something but don't return
};
// optional argument
let sum = (num1: number, num2: number, another?: number) => {
return num1 + num2;
};
sum(2, 3);
let func = (user: { username: string; age: number; phone?: string }) => {
console.log(user.username);
};
//// TYPE ALIASES
type UserType = {
username: string;
age: number;
phone?: string;
};
let betterFunc = (user: UserType) => {
console.log(user.username);
};
// func and betterFunc is same, betterFunc is more clean and readable
type UserType2 = {
username: string;
age: number;
phone?: string;
theme: "dark" | "light";
};
const userWithTheme: UserType2 = {
username: "john",
age: 43,
// theme:"pink"
theme: "dark",
};
//FUNCTION SIGNATURES
type myFunc = (a: number, b: string) => void;
let write: myFunc = (num, str) => {
console.log(num + " times " + str);
};
//// INTERFACES--> we can extends other interface
// Be aware no equal sign
interface IUser {
username: string;
email: string;
age: number;
}
const client: IUser = {
username: "tom",
email: "tom@gmail.com",
age: 43,
};
interface IEmployee extends IUser {
employeeId: number;
}
const emp: IEmployee = {
username: "tom",
email: "tom@gmail.com",
age: 43,
employeeId: 1,
};
//// GENERICS
interface IAuthor {
id: number;
username: string;
}
interface ICategory {
id: number;
title: string;
}
interface IPost {
id: number;
title: string;
desc: string;
extra: IAuthor[] | ICategory[];
}
interface IPostBetter<T> {
id: number;
title: string;
desc: string;
extra: T[];
}
const testMe: IPostBetter<string> = {
id: 1,
title: "post title",
desc: "post desc",
extra: ["str", "str2"],
};
interface IPostEvenBetter<T extends object> {
id: number;
title: string;
desc: string;
extra: T[]; // array of objects
}
const testMe2: IPostEvenBetter<{ id:number }> = {
id: 1,
title: "post title",
desc: "post desc",
extra: [{ id: 1 }],
};
const testMe3: IPostEvenBetter<IAuthor> = {
id: 1,
title: "post title",
desc: "post desc",
extra: [{ id: 1, username: "john" }],
};
const testMe4: IPostEvenBetter<ICategory> = {
id: 1,
title: "post title",
desc: "post desc",
extra: [{ id: 1, title: "cat" }],
};
React-typescript
adding types for Data (api call data)
// components/postList/PostList.ts
import React from "react";
import PostCard from "../postCard/PostCard";
import { PostProps } from "@/types/types";
async function getData() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
if (!res.ok) {
throw new Error("Failed to fetch data");
}
return res.json();
}
const PostList = async () => {
const data: PostProps[] = await getData();
return (
<div className="postList">
{/* <PostCard title="post title" body="post desc" /> */}
{data.map((post) => (
<PostCard key={post.id} {...post} />
))}
</div>
);
};
export default PostList;
// components/postCard/PostCard.ts
import { PostProps } from "@/types/types";
import React from "react";
const PostCard = ({title,body}: PostProps) => {
return (
<div className="postCard">
<h1>{title}</h1>
<p>{body}</p>
</div>
);
};
export default PostCard;
// types/types.ts
export type PostProps = { id: number; title: string; body: string };
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/app/1-props-example/page.tsx
component as children
react event
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/app/3-event-example/page.tsx
onChange -->search
onClick --> search button, delete button
how to find the event type? -> hover on -->onChange,onClick event
useState
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/app/4-usestate-example/page.tsx
useContext
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/app/5-usecontext-example/page.tsx
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/context/ThemeContext.tsx
useRef
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/app/6-useref-example/page.tsx
generic
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/components/generic/Item.tsx#L14
combined --> type and exclude
https://github1s.com/safak/typescript-tutorial/blob/main/react/src/components/exclude/Shape.tsx
1. useState
:
import React, { useState } from 'react';
const MyComponent: React.FC = () => {
const [count, setCount] = useState<number>(0); // specify type for state
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
2. useEffect
:
import React, { useState, useEffect } from 'react';
const MyComponent: React.FC = () => {
const [data, setData] = useState<string[]>([]);
useEffect(() => {
// fetch data from API
fetchData().then((result: string[]) => {
setData(result);
});
}, []); // dependency array to run effect only once
return (
<ul>
{data.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
};
3. useContext
:
import React, { useContext } from 'react';
import MyContext from './MyContext';
const MyComponent: React.FC = () => {
const { value } = useContext(MyContext);
return <p>Context value: {value}</p>;
};
4. useReducer
:
import React, { useReducer } from 'react';
interface State {
count: number;
}
type Action = { type: 'increment' } | { type: 'decrement' };
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
};
const MyComponent: React.FC = () => {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
};
5. useCallback
:
import React, { useState, useCallback } from 'react';
const MyComponent: React.FC = () => {
const [count, setCount] = useState<number>(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
6. useMemo
:
import React, { useMemo } from 'react';
const MyComponent: React.FC = () => {
const expensiveCalculation = useMemo(() => {
// Perform expensive calculation
return 10 * 20;
}, []);
return <p>Result: {expensiveCalculation}</p>;
};
7. useRef
:
import React, { useRef, useEffect } from 'react';
const MyComponent: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return <input ref={inputRef} />;
};
Using TypeScript with React, particularly with hooks and events, can provide a more robust and type-safe development experience. Here's a detailed guide on how to utilize TypeScript in React with hooks, handling events, and some commonly used production use cases:
1. Setting up TypeScript with React:
Install necessary dependencies:
npx create-react-app my-app --template typescript
This command creates a new React project configured with TypeScript.
2. Using TypeScript with React Hooks:
useState:
import React, { useState } from 'react';
const MyComponent: React.FC = () => {
const [count, setCount] = useState<number>(0); // specify type for state
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
useEffect:
import React, { useState, useEffect } from 'react';
const MyComponent: React.FC = () => {
const [data, setData] = useState<string[]>([]);
useEffect(() => {
// fetch data from API
fetchData().then((result) => {
setData(result);
});
}, []); // dependency array to run effect only once
return (
<ul>
{data.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
};
Handling Events:
Basic Event Handling:
import React from 'react';
const MyComponent: React.FC = () => {
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
console.log('Button clicked');
};
return (
<div>
<button onClick={handleClick}>Click me</button>
</div>
);
};
Controlled Components:
import React, { useState } from 'react';
const MyComponent: React.FC = () => {
const [inputValue, setInputValue] = useState<string>('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
};
return (
<div>
<input type="text" value={inputValue} onChange={handleChange} />
<p>Input value: {inputValue}</p>
</div>
);
};
Common Production Use Cases:
Form Handling with Validation:
import React, { useState } from 'react';
const MyForm: React.FC = () => {
const [email, setEmail] = useState<string>('');
const [error, setError] = useState<string | null>(null);
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!email.includes('@')) {
setError('Invalid email');
} else {
// Submit form data
}
};
return (
<form onSubmit={handleSubmit}>
<input type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
<button type="submit">Submit</button>
{error && <p>{error}</p>}
</form>
);
};
Fetching Data from an API:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const MyComponent: React.FC = () => {
const [data, setData] = useState<any[]>([]);
useEffect(() => {
axios.get('https://api.example.com/data')
.then(response => {
setData(response.data);
})
.catch(error => {
console.error('Error fetching data:', error);
});
}, []);
return (
<ul>
{data.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
};