Skip to content

Instantly share code, notes, and snippets.

@composite
Created February 17, 2024 05:44
Show Gist options
  • Save composite/cc45e88eba3c47363109dd3ae68c45af to your computer and use it in GitHub Desktop.
Save composite/cc45e88eba3c47363109dd3ae68c45af to your computer and use it in GitHub Desktop.
React Polymorphic Component for Dummies & IntersectionObserver based Component Example
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment */
'use client';
import type { ElementType, RefCallback } from 'react';
import { createElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { twMerge } from 'tailwind-merge';
import type { PolymorphicComponentProps } from './polymorphic';
export type InViewProps = {
/**
* The CSS Class that active when inside browser's sight
*/
inClass?: string;
/**
* The CSS Class that active when outside browser's sight
*/
outClass?: string;
/**
* Activate once when your component in your sight
*/
once?: boolean;
/**
* Intersection Observer rootMargin (you must obtain number with 'px' unit. example, 0px)
*/
rootMargin?: string;
/**
* The event fired when your component in your sight
*/
onView?: (el: HTMLElement) => void;
};
/**
* The polymorphic compoenent that Change CSS class even your component is on your sight.
*/
export default function InView<T extends ElementType = 'div'>({
as,
className,
children,
inClass,
once,
outClass,
rootMargin,
onView,
...props
}: PolymorphicComponentProps<T, InViewProps>) {
const ref = useRef<any>();
const exRef = useCallback<RefCallback<any>>((r) => {
ref.current = r;
}, []);
const [isView, setIsView] = useState(false);
const calcClass = useMemo(() => twMerge(className, isView ? inClass : outClass), [isView]);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
setIsView(entry.isIntersecting);
if (once && entry.isIntersecting && ref.current) observer.unobserve(ref.current);
onView && onView(ref.current as HTMLElement);
},
{ rootMargin: rootMargin || '-100px' }
);
ref.current && observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref]);
return createElement(as || 'div', { ...props, ref: exRef, className: calcClass }, children);
}
/* eslint-disable */
import type { ComponentPropsWithoutRef, ComponentPropsWithRef, ElementType } from 'react';
export type AsProp<T extends ElementType> = {
as?: T;
};
export type PolymorphicRef<T extends ElementType> = ComponentPropsWithRef<T>['ref'];
export type PolymorphicRefProps<T extends ElementType> = {
ref?: PolymorphicRef<T>;
};
export type PolymorphicComponentProps<T extends ElementType, Props = {}> = AsProp<T> &
ComponentPropsWithoutRef<T> &
Props;
export type PolymorphicRefComponentProps<T extends ElementType, Props = {}> = AsProp<T> &
ComponentPropsWithoutRef<T> &
Props &
PolymorphicRefProps<T>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment