Back to Blog
ReactยทHooksJavaScriptTypeScript

How to Use useEffectEvent Hook in React

Learn how to properly use the experimental useEffectEvent hook in React to handle events inside effects without triggering re-renders. A comprehensive guide with practical examples.

Ananas Studio
4 min read
How to Use useEffectEvent Hook in React

How to Use useEffectEvent Hook in React

React's useEffectEvent is an experimental hook that solves a common problem: reading the latest props or state inside an effect without re-running the effect when those values change.

The Problem It Solves

Consider this common scenario:

function ChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      showNotification('Connected!', theme);
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, theme]); // ๐Ÿ”ด Reconnects when theme changes!
}

In this example, changing the theme will reconnect to the chat room โ€” which is not what we want. We only want to reconnect when roomId changes.

The Solution: useEffectEvent

The useEffectEvent hook lets you extract non-reactive logic from your effects:

import { useEffectEvent } from 'react';

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showNotification('Connected!', theme);
  });

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      onConnected();
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]); // โœ… Only reconnects when roomId changes
}

Key Characteristics

1. Always Reads Latest Values

Effect Events always read the latest values of props and state:

function Timer({ delay, onTick }) {
  const tick = useEffectEvent(() => {
    onTick(); // Always calls the latest onTick
  });

  useEffect(() => {
    const id = setInterval(tick, delay);
    return () => clearInterval(id);
  }, [delay]); // โœ… No need to include onTick
}

2. Not a Dependency

Effect Events don't need to be listed in the dependency array:

// โœ… Correct - no need to add tick to dependencies
useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);
}, []); // tick is stable

3. Synchronous Execution Only

Effect Events should be called from inside effects, not during rendering:

// โœ… Correct
useEffect(() => {
  myEffectEvent();
}, []);

// ๐Ÿ”ด Wrong - called during render
return <button onClick={myEffectEvent}>Click</button>;

Practical Example: Analytics Tracking

Here's a real-world example of tracking page visits without re-triggering the effect:

function ProductPage({ productId, referrer, userId }) {
  const logVisit = useEffectEvent((visitedUrl) => {
    // Always reads latest userId and referrer
    logAnalytics(visitedUrl, {
      referrer,
      userId,
      timestamp: Date.now(),
    });
  });

  useEffect(() => {
    logVisit(`/product/${productId}`);
  }, [productId]); // โœ… Only logs when productId changes

  return <ProductDetails productId={productId} />;
}

When to Use useEffectEvent

Use CaseuseEffectEvent?
Read latest props/state in effectโœ… Yes
Callback passed to effect cleanupโœ… Yes
Event handlers called during renderโŒ No
Logic that should trigger effect re-runโŒ No

TypeScript Support

The hook is fully typed in React 18+:

import { useEffectEvent } from 'react';

interface NotificationOptions {
  message: string;
  type: 'success' | 'error' | 'info';
}

function useNotification(defaultType: NotificationOptions['type']) {
  const show = useEffectEvent((message: string) => {
    showToast({ message, type: defaultType });
  });

  return { show };
}

Important Notes

โš ๏ธ Experimental Feature: useEffectEvent is still experimental in React 18.x. The API may change before the final release.

To use it today, you may need to import from a canary release:

npm install react@canary react-dom@canary

Comparison with useCallback

FeatureuseCallbackuseEffectEvent
Returns stable referenceโœ… With depsโœ… Always
Reads latest valuesOnly with depsโœ… Always
Can be used in JSXโœ… YesโŒ No
Is a dependencyโœ… YesโŒ No

Conclusion

The useEffectEvent hook is a powerful addition to React's hooks arsenal. It elegantly solves the problem of reading latest values inside effects without causing unnecessary re-executions.

Key takeaways:

  1. Use useEffectEvent for non-reactive logic inside effects
  2. Effect Events always read the latest props and state
  3. They don't need to be listed as dependencies
  4. Only call them from inside effects, not during render

Want to learn more about React hooks and best practices? Contact us for custom training or consulting.

Tags:ReactHooksJavaScriptTypeScriptFrontend
A

Ananas Studio

Software Development Team

Building modern software solutions at Ananas Studio. We specialize in React, Next.js, and scalable architectures.

Related Articles

Continue reading with these related posts