DEV Community

Husnain Mustafa
Husnain Mustafa

Posted on • Updated on

Generics in React

What are generics

Have you defined a function/method in any language? If so, you probably know that we are able to define its parameters/arguments that are passed where function is called.

By this we call pass the whatever the value we want to pass to a function and we can decide it at spot.

But have you ever wondered what if we want to decide the type of some identifier at spot rather than defining it where we created our function. This is where Generics come. Generics are simply the passing of dynamic type to the function (or type/interface) which can be used inside a function.

It might seem a lot. Let's have it through the example:

Usually what we do is

const exmapleFunction = (data: string)=>{
    console.log(data)
}

exmapleFunction("Exmaple Function Called")

Enter fullscreen mode Exit fullscreen mode

In the above function, we can only pass the data as type of string. But we want data type to be dynamic. Here is what we do to make it dynamic:

const exmapleFunction = <T>(data: T) => {
  console.log(data);
};

exmapleFunction<Record<string, string>>({ id: '1', value: 'This is data' });

Enter fullscreen mode Exit fullscreen mode

Now we can pass the dynamic type where we am calling the function, similar to the argument passing.

Generics in React

Now let's make a component that uses Generics:

import { useState } from 'react';

type Props<T> = {
  data: T;
};

const MyComponent = <T,>(props: Props<T>) => {
  const [dataState, setDataState] = useState<T>(props.data);

  return <div></div>;
};

const App = () => {
  <MyComponent<string> data={'Example'}></MyComponent>;
};

Enter fullscreen mode Exit fullscreen mode

Now we have passed the dynamic type, generic where we am using the MyComponent.

Moving Further

Now we have learned that how we can make and pass the generics to our component.
But what if we want to use generics, but still restrict some type. We want to allow the generics that must satisfy some constraints. We can do it by using the extends keyword.
Here is how:

import { useState } from 'react';

type ExtendedDataType = {
    id: number
    value: string
}

type Props<T extends ExtendedDataType> = {
  data: T;
};

const MyComponent = <T extends ExtendedDataType,>(props: Props<T>) => {
  const [dataState, setDataState] = useState<T>(props.data);

  return <div></div>;
};

const App = () => {
  <MyComponent<string> data={'Example'}></MyComponent>;
};

Enter fullscreen mode Exit fullscreen mode

Now we defined a generic that must extends our ExtendedDataType, i.e the type we pass must have id as string and value as number in it. Hence the part of the code below shows the error, as string does not satisfy the constraint.

Image description

Let's pas the correct now:

import { useState } from 'react';

type ExtendedDataType = {
  id: number;
  value: string;
};

type Props<T extends ExtendedDataType> = {
  data: T;
};

const MyComponent = <T extends ExtendedDataType>(props: Props<T>) => {
  const [dataState, setDataState] = useState<T>(props.data);

  return <div></div>;
};

type AppDataType = {
  id: number;
  value: string;
  key: string;
};

const App = () => {
  <MyComponent<AppDataType> data={'Example'}></MyComponent>;
};
Enter fullscreen mode Exit fullscreen mode

Tada! Now we have correct type that is passed as generic. But notice we didn't change the value of data. That is why we are having this error:

Image description

Now we have to pass the data as type of AppDataType.
Here it is:

import { useState } from 'react';

type ExtendedDataType = {
  id: number;
  value: string;
};

type Props<T extends ExtendedDataType> = {
  data: T;
};

const MyComponent = <T extends ExtendedDataType>(props: Props<T>) => {
  const [dataState, setDataState] = useState<T>(props.data);

  return <div></div>;
};

type AppDataType = {
  id: number;
  value: string;
  key: string;
};

const appData = {
    id: 3,
    value: "appData",
    key: 'app-data-3'
}

const App = () => {
  <MyComponent<AppDataType> data={appData}></MyComponent>;
};
Enter fullscreen mode Exit fullscreen mode

Now we have everything as we needed.
But what if we want to have some default value for generics, so if generics is not passed, that default value is considered. We can simply do that by:

const MyComponent = <T extends ExtendedDataType = ExtendedDataType>(props: Props<T>) => {
  const [dataState, setDataState] = useState<T>(props.data);

  return <div></div>;
};
Enter fullscreen mode Exit fullscreen mode

Now even if we do not pass generics, we will have the T as ExtendedDataType:

const App = () => {
  <MyComponent data={{id: 3, value: 'value-3'}}></MyComponent>;
};

Enter fullscreen mode Exit fullscreen mode

Note that we have not passed the generics, so T would be ExtendedDataType and so data would be of type ExtendedDataType, and hence we cannot pass data as appData because appData has one extra feature key, which ExtendedDataType lacks.

That's it for today.
Hope it help you learned something.
Happy learning.

Top comments (2)

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

Edit your code blocks so they do syntax highlighting: After the 3 opening back ticks, add the language. This is a sample using "typescript" after the opening back ticks:

const App = () => {
  <MyComponent data={{id: 3, value: 'value-3'}}></MyComponent>;
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
babucarr profile image
babucarr33

This is what I call "good stuff!!!"