2.4.0 • Published 10 months ago

react-use-ref-effect v2.4.0

Weekly downloads
16
License
MIT
Repository
github
Last release
10 months ago

Executes an effect directly after React attaches a ref to a DOM node and provides cleanup functionality when React detaches the DOM node from the ref.

  • The hook does not trigger additional renderings.
  • The hook size is only 139b in v2.0.0.

Version 2.0.0 Update

React 19 adds cleanup functions for ref callbacks, so users should consider useing useCallback directly instead of the react-use-ref-effect package.

API

useRefEffect API

Use case: every time you need to react to ref changes with optional cleanup

  • const ref = useRefEffect(callback) - calls the provided callback when the ref is changed.

  • const ref = useRefEffect(callback, []) - calls the provided callback when the ref is changed or a dependency is changed - similar to useEffect.

  • const ref = useRefEffect((element) => { return cleanupCallback }, []) - calls the provided cleanupCallback once the component unmounts or when React removes the referenced DOM element.

import { useRefEffect } from 'react-use-ref-effect';

const Component = () => {
  const ref = useRefEffect((element) => {
    console.log('Element', element, 'is now available');
    return () => {
      console.log('Element', element, 'is no longer available');
    }
  }, []);

  return <div ref={ref}>Hello World</div>
}

useMergeRefs API

Use case: when you need to apply multiple refs to a single DOM element

  • const mergedRef = useMergeRefs(ref1, ref2, ...) - combines multiple refs into a single ref callback that applies all of them to the same element.
  • All cleanup functions from the individual refs are properly called when the element is unmounted or detached.
  • Cannot be mixed with refs from useRef()
import { useRefEffect, useMergeRefs } from 'react-use-ref-effect';

const Component = () => {
  // A ref for focus management
  const focusRef = useRefEffect((element) => {
    element.focus();
    return () => {
      console.log('Focus element unmounted');
    }
  }, []);

  // A ref for event listeners
  const eventRef = useRefEffect((element) => {
    const clickHandler = () => console.log('Element clicked');
    element.addEventListener('click', clickHandler);
    return () => {
      element.removeEventListener('click', clickHandler);
    }
  }, []);

  // Combine both refs into one
  const mergedRef = useMergeRefs(focusRef, eventRef);

  return <input ref={mergedRef} placeholder="I'll be focused and have a click handler" />
}

React 19 Compatibility

React 19 added native support for cleanup functions in ref callbacks. This package now leverages this feature to provide a simpler implementation while maintaining the same API.

If you're using React 19, you could achieve similar functionality with:

import { useCallback } from 'react';

const Component = () => {
  const ref = useCallback((element) => {
    if (element) {
      console.log('Element', element, 'is now available');
      return () => {
        console.log('Element', element, 'is no longer available');
      }
    }
  }, []);

  return <div ref={ref}>Hello World</div>
}

This package provides a convenient wrapper around this pattern with dependency tracking.

Motivation

React provides two powerful hooks: useRef and useEffect.
However before React 19, they didn't work perfectly in combination when dealing with conditional rendering:

// ⚠️ Pre-React 19 approach with issues
const ref = useRef();
useEffect(() => {
  // do something with ref.current
}, [ref.current])

The issues with this approach were:

🚫   Failed to execute the effect for conditionally rendered components
(e.g. isOpen && <span ref={ref}>Demo</span>)

🚫   Failed to execute the effect for lazy rendered components
(e.g. <LazyComponent><span ref={ref}>Demo</span></LazyComponent>)

🚫   Failed to execute the effect if a child controlled the time to mount
(e.g. <Slider waitFor={3000}><span ref={ref}>Demo</span></Slider>)

The useRefEffect hook solved these issues by providing a pattern recommended in the React hooks FAQ:

const ref = useRefEffect((element) => {
  // do something with element
  return () => {
    // cleanup
  }
}, [])

With React 19, this pattern is now supported natively through ref callbacks with cleanup, and this package provides a convenient wrapper around that functionality.

useRefEffectWithCurrent API

Use case: when you need to react to ref changes and maintain a reference to the current element

import { useRefEffectWithCurrent } from 'react-use-ref-effect';

const Component = () => {
  // Get both effect and current reference capabilities
  const ref = useRefEffectWithCurrent((element) => {
    console.log('Element attached:', element);
    return () => {
      console.log('Element detached:', element);
    }
  }, []);
  
  // You can access ref.current anytime, even in event handlers
  const handleClick = () => {
    if (ref.current) {
      console.log('Current element dimensions:', ref.current.getBoundingClientRect());
    }
  };

  return (
    <>
      <div ref={ref}>This element is accessible via ref.current</div>
      <button onClick={handleClick}>Log Element Dimensions</button>
    </>
  );
}

Similar packages:

License

MIT

2.3.0

10 months ago

2.2.1

10 months ago

2.2.0

10 months ago

2.3.2

10 months ago

2.4.0

10 months ago

2.3.1

10 months ago

2.1.0

10 months ago

2.0.1

10 months ago

2.0.0

10 months ago

1.3.0

2 years ago

1.2.0

4 years ago

1.1.0

5 years ago

1.0.0

5 years ago

0.2.0

7 years ago

0.1.0

7 years ago