Comprehensive collection of React hooks designed for modern React applications, offering utilities that extend beyond native React capabilities.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Utilities for working with React refs, including merging multiple refs and creating effect-based refs.
React hook to merge multiple React refs (either MutableRefObjects or ref callbacks) into a single ref callback that updates all provided refs. Essential for forwarding refs while also maintaining internal refs.
/**
* React hook to merge multiple React refs into a single ref callback that updates all provided refs
* @param refs - Refs to collectively update with one ref value.
* @returns A function with an attached "current" prop, so that it can be treated like a RefObject.
*/
function useMergedRefs<T>(...refs: (React.Ref<T> | undefined)[]): RefObjectFunction<T>;
/**
* A Ref function which can be treated like a ref object in that it has an attached
* current property, which will be updated as the ref is evaluated.
*/
type RefObjectFunction<T> = React.RefObject<T> & ((value: T) => void);Usage Examples:
import { useMergedRefs } from "@fluentui/react-hooks";
import { forwardRef } from "react";
// Basic ref forwarding with internal ref
const CustomInput = forwardRef<HTMLInputElement, { label: string }>((props, ref) => {
const internalRef = useRef<HTMLInputElement>(null);
const mergedRef = useMergedRefs(ref, internalRef);
const focusInput = () => {
internalRef.current?.focus();
};
return (
<div>
<label>{props.label}</label>
<input ref={mergedRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
});
// Multiple ref callbacks
function MultiRefComponent() {
const ref1 = useRef<HTMLDivElement>(null);
const ref2 = useRef<HTMLDivElement>(null);
const callbackRef1 = useCallback((element: HTMLDivElement | null) => {
if (element) {
console.log('Callback ref 1:', element);
}
}, []);
const callbackRef2 = useCallback((element: HTMLDivElement | null) => {
if (element) {
console.log('Callback ref 2:', element);
}
}, []);
const mergedRef = useMergedRefs(ref1, ref2, callbackRef1, callbackRef2);
return (
<div ref={mergedRef}>
This element is referenced by multiple refs
</div>
);
}
// HOC pattern with ref forwarding
function withLogging<T extends HTMLElement>(Component: React.ComponentType<any>) {
return forwardRef<T, any>((props, ref) => {
const loggingRef = useCallback((element: T | null) => {
if (element) {
console.log('Element attached:', element.tagName);
} else {
console.log('Element detached');
}
}, []);
const mergedRef = useMergedRefs(ref, loggingRef);
return <Component {...props} ref={mergedRef} />;
});
}Creates a ref, and calls a callback whenever the ref changes to a non-null value. The callback can optionally return a cleanup function that'll be called before the value changes, and when the ref is unmounted. This works around the limitation that useEffect cannot depend on ref.current.
/**
* Creates a ref, and calls a callback whenever the ref changes to a non-null value.
* The callback can optionally return a cleanup function.
* @param callback - Called whenever the ref's value changes to non-null. Can optionally return a cleanup function.
* @param initial - (Optional) The initial value for the ref.
* @returns A function that should be called to set the ref's value. Also has a .current member for access.
*/
function useRefEffect<T>(
callback: (value: T) => (() => void) | void,
initial?: T | null
): RefCallback<T>;
/**
* A callback ref function that also has a .current member for the ref's current value.
*/
type RefCallback<T> = ((value: T | null) => void) & React.RefObject<T>;Usage Examples:
import { useRefEffect } from "@fluentui/react-hooks";
// Basic DOM manipulation on element attach/detach
function AutoFocusComponent() {
const inputRef = useRefEffect<HTMLInputElement>(
(element) => {
// Called when element is attached
element.focus();
// Return cleanup function (optional)
return () => {
console.log('Input element is being detached');
};
}
);
return <input ref={inputRef} placeholder="Auto-focused" />;
}
// Intersection Observer setup
function VisibilityTracker({ onVisibilityChange }) {
const elementRef = useRefEffect<HTMLDivElement>(
(element) => {
const observer = new IntersectionObserver(
(entries) => {
const isVisible = entries[0].isIntersecting;
onVisibilityChange(isVisible);
},
{ threshold: 0.1 }
);
observer.observe(element);
// Cleanup: disconnect observer
return () => {
observer.disconnect();
};
}
);
return (
<div ref={elementRef} style={{ height: '200px', background: 'lightblue' }}>
Visibility tracked element
</div>
);
}
// Resize Observer
function ResizeTracker() {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const elementRef = useRefEffect<HTMLDivElement>(
(element) => {
const resizeObserver = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
setDimensions({ width, height });
});
resizeObserver.observe(element);
return () => {
resizeObserver.disconnect();
};
}
);
return (
<div>
<div
ref={elementRef}
style={{
resize: 'both',
overflow: 'auto',
border: '1px solid #ccc',
minWidth: '100px',
minHeight: '100px'
}}
>
Resizable element
</div>
<div>Dimensions: {dimensions.width} x {dimensions.height}</div>
</div>
);
}
// Canvas setup with cleanup
function CanvasComponent() {
const canvasRef = useRefEffect<HTMLCanvasElement>(
(canvas) => {
const context = canvas.getContext('2d');
if (!context) return;
// Setup canvas
canvas.width = 400;
canvas.height = 300;
// Draw something
context.fillStyle = 'blue';
context.fillRect(10, 10, 100, 100);
// Animation loop
let animationId: number;
const animate = () => {
// Animation logic here
animationId = requestAnimationFrame(animate);
};
animate();
// Cleanup animation
return () => {
cancelAnimationFrame(animationId);
};
}
);
return <canvas ref={canvasRef} />;
}
// Third-party library integration
function ChartComponent({ data }) {
const chartRef = useRefEffect<HTMLDivElement>(
(element) => {
// Initialize chart library
const chart = new SomeChartLibrary(element, {
data,
responsive: true
});
// Cleanup chart instance
return () => {
chart.destroy();
};
}
);
return <div ref={chartRef} style={{ width: '100%', height: '400px' }} />;
}