Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create useDynamicComponentRef hook #111

Open
leroykorterink opened this issue Mar 28, 2023 · 2 comments
Open

Create useDynamicComponentRef hook #111

leroykorterink opened this issue Mar 28, 2023 · 2 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@leroykorterink
Copy link
Collaborator

leroykorterink commented Mar 28, 2023

import { type MutableRefObject, useEffect, useId } from 'react';

/**
 * This hook generates an id for a component and then watches the DOM mutations to find it as soon as it's loaded. This
 * is commonly used for lazy loaded components.
 */
export function useDynamicComponentRef(ref: MutableRefObject<unknown>): string {
  const id = useId();

  useEffect(() => {
    const mutationObserver = new MutationObserver(() => {
      ref.current = document.querySelector(`[data-id="${id}"]`);

      if (ref.current) {
        mutationObserver.disconnect();
      }
    });

    mutationObserver.observe(
      // TODO: This could be the parent element of the dynasmic component to reduce the amount of DOM mutations being observed.
      document.body,
      {
        childList: true,

      // NOTE: If we only allow to use this hook on the parent element of dynamic component we can set this to false.
        subtree: true,
      },
    );

    return (): void => {
      mutationObserver.disconnect();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id]);

  return id;
}
@leroykorterink leroykorterink added enhancement New feature or request help wanted Extra attention is needed labels Mar 28, 2023
@leroykorterink
Copy link
Collaborator Author

leroykorterink commented Mar 28, 2023

Something like this should be more efficient.

import { useEffect, useId, type MutableRefObject, type RefObject } from 'react';

/**
 * This hook generates an id for a component and then watches the DOM mutations to find it as soon as it's loaded. This
 * is commonly used for lazy loading SVGs into the application. Just make sure to assign the returned id value to
 * the `data-id` on the lazy loaded component.
 *
 * @param ref
 */
export function useDynamicComponentRef(
  ref: MutableRefObject<Element | null>,
  parent: RefObject<Element>,
): string {
  const id = useId();

  useEffect(() => {
    if (parent.current === null) {
      throw new Error("The parent element can't be null");
    }

    const mutationObserver = new MutationObserver(() => {
      ref.current = document.querySelector(`[data-id="${id}"]`);

      if (ref.current) {
        mutationObserver.disconnect();
      }
    });

    mutationObserver.observe(parent.current, {
      childList: true,
    });

    return (): void => {
      mutationObserver.disconnect();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id]);

  return id;
}

@ThaNarie
Copy link
Member

A common use case for getting refs for lazy components, so do something with them as soon as they are loaded. For example, to add them to dependencies of useEffect to update the animation, etc.

The downside of using a ref for this, is that you won't be notified of any changes.


Can we document use cases for this hook, so we know if the parent is always available. And if it is, to check if we don't have an easier way to detect if it's loaded, for example when we have direct access to the element and can use the onLoad or something similar.

@leroykorterink leroykorterink changed the title Create useDynamicComponentRef hook Create useDynamicComponentRef hook Mar 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants