useSingleton
The React documentation’s “Avoiding recreating the ref contents” pattern as a hook.
Before:
function Video() {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
// ...After:
function Video() {
const playerRef = useSingleton(() => new VideoPlayer());
// ...Usage
0
import { useCallback, useEffect } from 'react';
import { useSingleton } from 'foxact/use-singleton';
interface VideoProps {
videoSrc: string;
}
function Video({ videoSrc }: VideoProps) {
const playerRef = useSingleton(() => new VideoPlayer());
// callback ref to attach the player to the video element
const videoElementCallbackRef = useCallback((el: HTMLVideoElement | null) => {
if (el) {
playerRef.current.attach(el);
playerRef.current.load(videoSrc);
} else {
playerRef.current.destroy();
}
}, [videoSrc]);
return <video ref={videoElementCallbackRef} />;
}Delayed initialization
If you want to delay the initialization, you can return null or undefined in the initializer function and let useSingleton retry on the next render.
import { useState } from 'react';
import { useSingleton } from 'foxact/use-singleton';
function Component() {
const [shouldInit, setShouldInit] = useState(false);
const workerRef = useSingleton(() => {
if (!shouldInit) {
return null;
}
return HeavyInstance.create();
});
if (!shouldInit) {
return (
<button onClick={() => setShouldInit(true)}>
Initialize
</button>
);
return <div />
}If you want to use useSingleton to conditionally create a singleton instance and don’t want to retry, you should return a non-nullish value:
const kNoInitialize = Symbol('no initialize');
function Component() {
const playerRef = useSingleton<typeof kNoInitialize | VideoPlayer>(() => (Math.random() > 0.5 ? kNoInitialize : new VideoPlayer()));
}