
useEffect React and TypeScript
By @Johnatan
React + Typescript
Using useEffect
in a React component with TypeScript involves ensuring that your effect functions, dependencies, and cleanup functions are properly typed. Here’s how you can do this:
Basic Example
Here's a simple example of using useEffect
with TypeScript:
typescript
import React, { useEffect, useState } from 'react';
const MyComponent: React.FC = () => {
const [data, setData] = useState<string | null>(null);
useEffect(() => {
// Fetching data
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
// Cleanup (if necessary)
return () => {
console.log('Cleanup if necessary');
};
}, []); // Dependency array
return <div>Data: {data}</div>;
};
export default MyComponent;
Typing the State
When using state, you need to specify the type of the state variable. In the example above, useState<string | null>
specifies that the state can be a string or null.
Example with Dependencies
If you have dependencies, you should also type them:
typescript
import React, { useEffect, useState } from 'react';
const MyComponent: React.FC = () => {
const [count, setCount] = useState<number>(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // Re-run the effect only if count changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default MyComponent;
Cleanup Function
Here’s an example with a cleanup function, typically used for subscriptions or timers:
typescript
import React, { useEffect, useState } from 'react';
const TimerComponent: React.FC = () => {
const [seconds, setSeconds] = useState<number>(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
return () => {
clearInterval(interval);
};
}, []); // Run once on mount and cleanup on unmount
return <div>Seconds: {seconds}</div>;
};
export default TimerComponent;
Fetching Data with TypeScript
For data fetching, you can define the type of the data you expect:
typescript
import React, { useEffect, useState } from 'react';
interface Data {
id: number;
name: string;
}
const FetchDataComponent: React.FC = () => {
const [data, setData] = useState<Data | null>(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then((data: Data) => setData(data))
.catch(error => console.error(error));
return () => {
// Any necessary cleanup
};
}, []); // Empty dependency array ensures this runs once on mount
return <div>{data ? `Name: ${data.name}` : 'Loading...'}</div>;
};
export default FetchDataComponent;
Handling Subscriptions
If you’re dealing with subscriptions, you can type the cleanup function appropriately:
typescript
import React, { useEffect, useState } from 'react';
const SubscriptionComponent: React.FC = () => {
const [message, setMessage] = useState<string>('');
useEffect(() => {
const subscription = subscribeToMessages((newMessage: string) => {
setMessage(newMessage);
});
return () => {
subscription.unsubscribe();
};
}, []); // Empty array ensures the effect runs only once
return <div>Message: {message}</div>;
};
function subscribeToMessages(callback: (message: string) => void) {
// Dummy subscription function
const interval = setInterval(() => {
callback('New message received');
}, 1000);
return {
unsubscribe: () => clearInterval(interval),
};
}
export default SubscriptionComponent;
Summary
- Type the state variables: Ensure that the state types are correctly defined.
- Type the data: When fetching data, define the type of the data you expect to receive.
- Type the cleanup function: Ensure that any cleanup functions are typed correctly.
By following these guidelines, you can effectively use useEffect
in your TypeScript React components.
html
<button class="group relative inline-flex h-12 w-12 items-center justify-center overflow-hidden rounded-full bg-neutral-950 font-medium text-neutral-200 transition-all duration-300 hover:w-32"><div class="inline-flex whitespace-nowrap opacity-0 transition-all duration-200 group-hover:-translate-x-3 group-hover:opacity-100">Hover me</div><div class="absolute right-3.5"><svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5"><path d="M8.14645 3.14645C8.34171 2.95118 8.65829 2.95118 8.85355 3.14645L12.8536 7.14645C13.0488 7.34171 13.0488 7.65829 12.8536 7.85355L8.85355 11.8536C8.65829 12.0488 8.34171 12.0488 8.14645 11.8536C7.95118 11.6583 7.95118 11.3417 8.14645 11.1464L11.2929 8H2.5C2.22386 8 2 7.77614 2 7.5C2 7.22386 2.22386 7 2.5 7H11.2929L8.14645 3.85355C7.95118 3.65829 7.95118 3.34171 8.14645 3.14645Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></div></button>
Published: 2024-06-10