expo-modules-core
Version:
The core of Expo Modules architecture
66 lines (54 loc) • 2.2 kB
text/typescript
'use client';
import { DependencyList, useEffect, useMemo, useRef } from 'react';
import type { SharedObject } from '../ts-declarations/SharedObject';
/**
* Returns a shared object, which is automatically cleaned up when the component is unmounted.
*/
export function useReleasingSharedObject<TSharedObject extends SharedObject>(
factory: () => TSharedObject,
dependencies: DependencyList
): TSharedObject {
const objectRef = useRef<TSharedObject | null>(null);
const objectRefToRelease = useRef<TSharedObject | null>(null);
const isFastRefresh = useRef(false);
const previousDependencies = useRef<DependencyList>(dependencies);
if (objectRef.current == null) {
objectRef.current = factory();
}
const object = useMemo(() => {
let newObject = objectRef.current;
const dependenciesAreEqual =
previousDependencies.current?.length === dependencies.length &&
dependencies.every((value, index) => value === previousDependencies.current[index]);
// If the dependencies have changed, schedule the previous object for release and create a new one,
// otherwise this has been called because of an unrelated fast refresh, and we don't want to release the object.
if (!newObject || !dependenciesAreEqual) {
objectRefToRelease.current = objectRef.current;
newObject = factory();
objectRef.current = newObject;
previousDependencies.current = dependencies;
}
return newObject;
}, dependencies);
useEffect(() => {
// When the object changes, release the previous one - it is important to do this in a useEffect, so that we don't release
// the object during render.
if (objectRefToRelease.current) {
objectRefToRelease.current.release();
objectRefToRelease.current = null;
}
}, [object]);
useMemo(() => {
isFastRefresh.current = true;
}, []);
useEffect(() => {
isFastRefresh.current = false;
return () => {
// This will be called on every fast refresh and on unmount, but we only want to release the object on unmount.
if (!isFastRefresh.current && objectRef.current) {
objectRef.current.release();
}
};
}, []);
return object;
}