This repository contains various examples and exercises to help you learn TypeScript, with a focus on React concepts and features. The following topics are covered:
- Class Components
- Props and Component as Props
- React Hooks (useState, useReducer, useRef)
- Context API
- Custom Components
- Generics and Polymorphic Components
- Restricting Props
- Template Literals in TypeScript
- Class Components
- Props and Component as Props
- React Hooks
- Context API
- Custom Components
- Generics and Polymorphic Components
- Restricting Props
- Template Literals
Class components in React are ES6 classes that extend React.Component
. Here is an example:
import React, { Component } from 'react';
interface MyComponentProps {
message: string;
}
class MyComponent extends Component<MyComponentProps> {
render() {
return <h1>{this.props.message}</h1>;
}
}
Props are used to pass data into a component. A component can also accept other components as props.
Example of passing data as props:
interface MyComponentProps {
message: string;
}
const MyComponent = ({ message }: MyComponentProps) => {
return <h1>{message}</h1>;
};
Passing a component as a prop:
interface ButtonProps {
label: string;
onClick: () => void;
}
const Button: React.FC<ButtonProps> = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
const Parent = () => {
return (
<Button label="Click Me" onClick={() => alert('Button clicked!')} />
);
};
The useState
hook allows you to add state to functional components.
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
useReducer
is similar to useState
but gives you more control over state updates, ideal for complex state logic.
import React, { useReducer } from 'react';
const reducer = (state: number, action: string): number => {
switch (action) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
return state;
}
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, 0);
return (
<div>
<p>Count: {state}</p>
<button onClick={() => dispatch('increment')}>Increment</button>
<button onClick={() => dispatch('decrement')}>Decrement</button>
</div>
);
};
The useRef
hook allows you to persist values between renders without triggering re-renders.
import React, { useRef } from 'react';
const FocusInput = () => {
const inputRef = useRef<HTMLInputElement>(null);
const handleFocus = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<input ref={inputRef} />
<button onClick={handleFocus}>Focus the input</button>
</div>
);
};
The Context API allows you to share state across your component tree without passing props down manually.
import React, { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
const App = () => {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
<ChildComponent />
</ThemeContext.Provider>
);
};
const ChildComponent = () => {
const theme = useContext(ThemeContext);
return <div>The current theme is {theme}</div>;
};
Custom components are React components that you create to encapsulate specific logic or UI elements.
interface CardProps {
title: string;
content: string;
}
const Card: React.FC<CardProps> = ({ title, content }) => (
<div>
<h3>{title}</h3>
<p>{content}</p>
</div>
);
const App = () => (
<Card title="Card Title" content="This is the content of the card." />
);
Generics allow you to define components that work with a variety of types.
interface ButtonProps<T> {
label: string;
onClick: (value: T) => void;
}
const Button = <T,>({ label, onClick }: ButtonProps<T>) => {
return <button onClick={() => onClick('clicked' as T)}>{label}</button>;
};
const App = () => {
const handleClick = (value: string) => alert(value);
return <Button label="Click Me" onClick={handleClick} />;
};
You can restrict the values that a prop can accept using TypeScript types.
interface ButtonProps {
type: 'button' | 'submit' | 'reset';
}
const Button = ({ type }: ButtonProps) => (
<button type={type}>Click Me</button>
);
Template literals allow you to embed expressions within string literals.
const name = 'John';
const greeting = `Hello, ${name}! Welcome to TypeScript.`;
console.log(greeting); // Output: Hello, John! Welcome to TypeScript.