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

https://github1s.com/safak/typescript-tutorial/blob/main/react/src/app/2-children-prop-example/page.tsx

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>
  );
};