Building Reusable Components in React with TypeScript Generics
Last Updated :
09 Sep, 2024
In modern web development, reusability is necessary to making capable, and modifiable applications. TypeScript, with its robust typing system, and React, with its component-based architecture, give dynamic tools to reach reusability. One of these tools is Generics. Generics permit developers to make reusable components, hooks, and functions that can work with any data type while confirming type safety.
Building Reusable Components in React with TypeScript GenericsThese are the following topics that we are going to discuss:
Merits of Generics
- Improved Type Safety: Generics provide type safety because it assures that the types you use in your code are trustworthy. It helps in detecting type-related errors at compile time instead of run-time! As an example, using the fact that if you are creating some sort of generic function that works with a list of items then TypeScript makes sure you pass the correct type or types few different situations as its effects have to be passed down and reduce error margins at run time thus increasing correctness throughout your code.
- Augmented Code Reusability: Generics permit to write code that can manage multiple data types without breaking down type safety. This directs to more reusable components and functions. As an instance, a generic 'Array' type can be implemented with any sort of elements, such as numbers, strings, or custom objects, making it adjustable for multiple instances. In React, this shows you can create components and hooks that work with any data type, thus it lowers repetition and advances stability across your codebase.
- Sustainability: One of the major merits of using generics is the reduction of code duplication, resulting in more manageable code. When your code is generic, it is simpler to manage because any modification you require to make only has to be made in one place instead of multiple versions of similar code. Hence, generics make your code more clear, as the type constraints define how the code should be used.
- Design Versatile Component: In React, generics permit us to create reusable components. As an instance, a generic 'List' component can be designed to render varied types of data without being rewritten for each type. By using generics, you can define the component's props and state in a way that fits distinct types, allowing you to manage varied data structures with a solo component.
Exploring Generics in TypeScript
Generics in TypeScript let developers make dynamic, reusable components or functions that can manage distinct data types while maintaining strict type safety. This will be managed by defining a generic type that acts as a substitute for a specific type, permitting the same piece of code to work with multiple types of data.
Setting Up a TypeScript Project
- Open your terminal or command prompt and type this command
cd my-project
- Write this command and install TypeScript and types for Node.js
npm install typescript @types/node --save-dev
- This command generates a 'tsconfig.json' file, which is used to configure TypeScript settings
npx tsc --init
- Compile the TypeScript code into javascript by writing this command
npx tsc
Basic Syntax of Generics
A generic function or component in TypeScript uses angle brackets (<>) to denote the generic type parameter. For an example:
function identity<T>(value: T): T {
return value;
}
Where, 'T' is a generic type parameter, and identity is a function that receives a parameter of type 'T' and returns a value of the same type.
Folder Structure:
Project structureExample: A easy Generic Function that returns the argument passed to it.
HTML
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>TypeScript Generic Function Example</title>
</head>
<body>
<script src="app.js"></script>
</body>
</html>
JavaScript
//app.js
function identity(arg) {
return arg;
}
let stringIdentity = identity("Hello GFG from TypeScript.");
let numberIdentity = identity(581);
console.log(stringIdentity);
console.log(numberIdentity);
JavaScript
//app.ts
function identity<T>(arg: T): T {
return arg;
}
const stringIdentity = identity("Hello GFG from TypeScript.");
const numberIdentity = identity(581);
console.log(stringIdentity);
console.log(numberIdentity);
Explanation:
- This given code defines a generic function identity that returns the exact value passed to it. It then calls this function with a string and a number, logging the results to the console.
- 'T' is a generic type parameter which acts as a placeholder for the type that will be provided when the function is called.
- The 'identity' function takes a single argument 'arg' of type 'T' and returns it.
- The function is apply with a string and a number, showing how it can manage multiple types.
Output:
OutputUsing Generics in React Components
Generics in Typescript will be more important when we are used in react components, this will permit developers to make reusable UI elements that can manage multiple data types while keeping strong type safety. By using generics, we can design components that are not tied to a particular data type but can fit to various types as required. Hence, this makes your react components more dynamic or reusable between varied parts of your application.
Setting Up the React Project with TypeScript
- Open your terminal or command prompt and run the given command to create a new React project using TypeScript.
npx create-react-app react-project --template typescript
- This command navigate you to the Project Directory
cd react-project
Folder Structure:
Project structureExample: Let us create a generic ' List ' component that can show a list of items of any type.
JavaScript
//src/components/GenericComponent.tsx
import React from 'react';
// A Generic Component
type Props<T> = {
items: T[];
renderItem: (item: T) => React.ReactNode;
};
function GenericComponent<T>({ items, renderItem }: Props<T>): JSX.Element {
return <div>{items.map(renderItem)}</div>;
}
export default GenericComponent;
JavaScript
//This file demonstrates the usage of the 'genericComponent'
//src/App.tsx
import React from 'react';
import GenericComponent from './components/GenericComponent';
const App = () => (
<GenericComponent
items={[101, 102, 103]}
renderItem={(item) => <div key={item}>{item}</div>}
/>
);
export default App;
JavaScript
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
The given generic component 'GenericComponent.tsx' file accepts an array of items and renderItem function as props. The 'renderItem' function is responsible to render every item in the array. In the 'App.tsx' file uses the 'GenericComponent.tsx' component to render a list of numbers (101, 102, and 103) and display. Each number is moved to the 'renderItem' function to build a div element. The 'index.tsx' file is the access point of the React application. It renders the 'App.tsx' component within the root element of the HTML.
Run the application by this using command:
npm start
Output:
OutputBuilding Reusable Hooks with Generics
React Hooks are a core feature in React that permit you to reuse stateful logic across your components. By applying Hooks, you can handle state and side effects in function components, which were previously handled by class components. When you use generics inside hooks, you make them more dynamic and letting the same hook to work with varied data types while keeping type safety. Let us immerse into forming a reusable hook that operates an array of items using a generics.
Folder Structure:
Project tsructureUpdated Dependencies:
//Package.json File
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.105",
"@types/react": "^18.3.4",
"@types/react-dom": "^18.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
Example: This example shows the Building Reusable Hooks with Generics.
JavaScript
//src/hooks/useArray.ts
import { useState } from 'react';
function useArray<T>(initialArray: T[]) {
const [array, setArray] = useState<T[]>(initialArray);
const add = (item: T) => setArray([...array, item]);
const remove = (index: number) => setArray(array.filter((_, i) => i !== index));
const clear = () => setArray([]);
return { array, add, remove, clear };
}
export default useArray;
JavaScript
// src/App.tsx
import React from 'react';
import useArray from './hooks/useArray';
const App: React.FC = () => {
const { array, add, remove, clear } = useArray<number>([5, 6, 7]);
return (
<div>
<h5>Array Management with Custom Hook</h5>
<ul>
{array.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
<button onClick={() => add(8)}>add 8</button>
<button onClick={() => remove(0)}>remove first</button>
<button onClick={clear}>clear</button>
</div>
);
};
export default App;
- This custom hook 'useArray' permits you to manage an array state with operations such as adding, removing, and clearing items.
- App.tsx file uses the 'useArray' hook to manage an array of numbers. It shows the array in a list and provides buttons to add a new item, remove the first item, and clear the entire array.
Output:
OutputThis project explains you how to create a 'type-safe' form builder using TypeScript and React. This build a form where fields are dynamically handled using generics in TypeScript.
Note: You have to follow the same steps as you followed earlier to create an application.
Folder Structure:
Project structureUpdated dependencies:
//package.json file
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.106",
"@types/react": "^18.3.4",
"@types/react-dom": "^18.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
Example: This example shows the Building a Type-Safe Form Builder.
JavaScript
// src/types/FormValues.ts
export type FormValues = {
name: string;
age: number;
};
JavaScript
// src/components/FormField.tsx
import React, { ChangeEvent } from 'react';
type FormFieldProps<T extends Record<string, any>> = {
name: keyof T;
value: T[keyof T];
onChange: (name: keyof T, value: T[keyof T]) => void;
};
function FormField<T extends Record<string, any>>({ name, value, onChange }: FormFieldProps<T>) {
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange(name, event.target.value as T[keyof T]);
};
return (
<div>
<label>{String(name)}</label>
<input type="text" value={String(value)} onChange={handleChange} />
</div>
);
}
export default FormField;
JavaScript
// src/components/FormBuilder.tsx
import React, { useState } from 'react';
import FormField from './FormField';
import { FormValues } from '../types/FormValues';
function FormBuilder() {
const [values, setValues] = useState<FormValues>({
name: '',
age: 0,
});
const handleChange = (name: keyof FormValues, value: FormValues[keyof FormValues]) => {
setValues((prevValues) => ({ ...prevValues, [name]: value }));
};
return (
<form>
<FormField name="name" value={values.name} onChange={handleChange} />
<FormField name="age" value={values.age} onChange={handleChange} />
</form>
);
}
export default FormBuilder;
JavaScript
// src/App.tsx
import React from 'react';
import FormBuilder from './components/FormBuilder';
function App() {
return (
<div className="App">
<FormBuilder />
</div>
);
}
export default App;
JavaScript
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
By using this command run the code:
npm start
Output:
OutputBest way of Using Generics in TypeScript and React
- Initiate with Specific Types and then Generalize: Initiate by applying an function, component, and hook with specific types. This will help in prioritize on the basic feature before adding complexity with generics. Once the feature is executing as assumed, recode the code to use generics. It permits the same logic to work with multiple types, which strengthening reusability.
- Use Informative Generic Names: Use informative names for your generic types. Rather than using single-letter names such as T, U, or V, prefer using more meaningful names like 'ItemType', 'ResponseType', or 'InputType'. Meaningful names strengthen the readability and make it simpler to know the ambition of the generic type.
- Keep Generics as minimal as Possible: Avoid making generics more complicated than necessary. If a component and function becomes more complex with multiple generic types, determine if it can be eased or divided into segments. Assure that the use of generics does not make the code difficult to get or read. Thus, the target is to manage reusability with maintainability.
- Lessens Generics usage: As we know generics improve code reusability, using them too much can cause difficult and confusing to read code. Use generics only when requisite and choose exact types when the type requirements are basic. Unnecessary use of generics can make the codebase difficult to analyze and handle.
Conclusion
Generics in TypeScript or React are robust tools for designing reusable and type-safe components or hooks. By learning this and using generics, you will be able to design more dynamic and modifiable applications. This will not only better code quality but also perfects development capability by providing you to write hooks and components that work with multiple data types.
Similar Reads
Function Overloading With Generics In TypeScript
TypeScript has the ability to overload functions which means multiple signatures may be defined for one single function, this comes in handy when you want a function to behave in a certain way based on the provided parameters. In this article, we will be looking at how function overloading is done u
4 min read
TypeScript Using Type Parameters in Generic Constraints
TypeScript Generics allows you to write reusable code that works with different types. In this article, we will explore how to use type parameters in generic constraints to ensure that which types are allowed in generic types or functions. Syntax:function functionName(arg: Type): ReturnType { // Fun
2 min read
TypeScript with React: Benefits and Best Practices
TypeScript, a superset of JavaScript, is gaining traction in the React community. When integrated into React projects, TypeScript offers significant benefits such as static typing, error reduction, and improved developer experience. If you want to write more maintainable, safer, and scalable code, u
4 min read
Building a Component Library with React Hooks and Storybook
Building a component library is a powerful way to streamline the development process, ensure consistency across projects, and promote the reusability of UI elements. In this article, we'll explore how to create a component library using React Hooks and Storybook, a tool for developing UI components
3 min read
TypeScript Using Class Types in Generics
TypeScript Using class types in generics allows you to create more flexible and reusable code by specifying that a generic parameter should be a class constructor. This is particularly useful when you want to work with instances of classes but want to keep the code type safe. You can define a generi
3 min read
What is Recursive Generic in TypeScript ?
In TypeScript, a recursive generic is a type that refers to itself within its definition, enabling the creation of data structures or types containing references to the same type within their structure. This capability proves invaluable when dealing with nested or hierarchical data structures like t
4 min read
How to use refs in React with TypeScript ?
To use refs in React with TypeScript, you can take advantage of the ref attribute to directly interact with DOM elements or child components. Refs provide a way to access the underlying DOM node or React component instance and can be especially useful when you need to manage focus, control animation
4 min read
How to make Reusable React Components ?
ReactJS is a JavaScript library used to build single-page applications (SPAs). React makes it easy to create an interactive user interface by using a component-based approach. In this article, we will learn about Reusable React Components with examples. Table of Content What are Reusable React Compo
4 min read
10 Compelling Reasons to Use TypeScript with React in 2024: A Developer's Guide
While React is getting better with time, TypeScript has become the tool that developers can use to create better applications. Thus, it becomes clear that in 2024, the use of React + TypeScript becomes even more advantageous, and is not limited to the ability to catch errors already in the developme
7 min read
How to Extend abstract class with Generics in Typescript ?
In Typescript, an abstract class is the base class that is inherited by other classes without having to define its members. Generic is the feature with which you can create a variable that represents a type in classes, functions, and type aliases that don't need to define the types that they use exp
3 min read