feat: create useRiveFile hook

This commit is contained in:
Adam
2024-05-23 17:11:50 -07:00
committed by hernan
parent 4678ea9ecf
commit 16dc257b2f
4 changed files with 140 additions and 3 deletions

38
src/hooks/useRiveFile.ts Normal file
View File

@@ -0,0 +1,38 @@
import { useState, useEffect } from 'react';
import type { UseRiveFileParameters } from '../types';
import { RiveFile } from '@rive-app/canvas';
/**
* Custom hook for initializing and managing a RiveFile instance within a component.
* It sets up a RiveFile based on provided source parameters (URL or ArrayBuffer) and ensures
* proper cleanup to avoid memory leaks when the component unmounts or inputs change.
*
* @param params - Object containing parameters accepted by the Rive file in the rive-js runtime,
*
* @returns {RiveFile} Contains the active RiveFile instance (`riveFile`).
*/
function useRiveFile(params: UseRiveFileParameters) {
const [riveFile, setRiveFile] = useState<RiveFile | null>(null);
useEffect(() => {
let file: RiveFile | null = null;
const loadRiveFile = async () => {
file = new RiveFile(params);
setRiveFile(file);
};
loadRiveFile();
return () => {
if (file) {
file.cleanup();
}
};
}, [params.src, params.buffer]);
return { riveFile };
}
export default useRiveFile;

View File

@@ -2,8 +2,9 @@ import Rive, { RiveProps } from './components/Rive';
import useRive from './hooks/useRive';
import useStateMachineInput from './hooks/useStateMachineInput';
import useResizeCanvas from './hooks/useResizeCanvas';
import useRiveFile from './hooks/useRiveFile';
export default Rive;
export { useRive, useStateMachineInput, useResizeCanvas, RiveProps };
export { RiveState, UseRiveParameters, UseRiveOptions } from './types';
export { useRive, useStateMachineInput, useResizeCanvas, useRiveFile , RiveProps };
export { RiveState, UseRiveParameters, UseRiveFileParameters, UseRiveOptions } from './types';
export * from '@rive-app/canvas';

View File

@@ -1,5 +1,5 @@
import { RefCallback, ComponentProps } from 'react';
import { Rive, RiveParameters } from '@rive-app/canvas';
import { Rive, RiveParameters, RiveFileParameters } from '@rive-app/canvas';
export type UseRiveParameters = Partial<Omit<RiveParameters, 'canvas'>> | null;
@@ -36,3 +36,6 @@ export type RiveState = {
rive: Rive | null;
RiveComponent: (props: ComponentProps<'canvas'>) => JSX.Element;
};
export type UseRiveFileParameters = RiveFileParameters;

95
test/useRiveFile.test.tsx Normal file
View File

@@ -0,0 +1,95 @@
import { renderHook } from '@testing-library/react-hooks';
import { mocked } from 'jest-mock';
import useRiveFile from '../src/hooks/useRiveFile';
import { RiveFile } from '@rive-app/canvas';
jest.mock('@rive-app/canvas', () => ({
RiveFile: jest.fn().mockImplementation(() => ({
cleanup: jest.fn(),
})),
}));
describe('useRiveFile', () => {
beforeEach(() => {
mocked(RiveFile).mockClear();
});
it('initializes RiveFile with provided parameters', async () => {
const params = {
src: 'file-src',
enableRiveAssetCDN: false
};
const { result } = renderHook(() => useRiveFile(params));
expect(RiveFile).toHaveBeenCalledWith(params);
expect(result.current.riveFile).toBeDefined();
});
it('cleans up RiveFile on unmount', async () => {
const params = {
src: 'file-src',
enableRiveAssetCDN: false
};
const { result, unmount } = renderHook(() => useRiveFile(params));
const riveInstance = result.current.riveFile;
expect(riveInstance).toBeDefined();
unmount();
expect(riveInstance?.cleanup).toHaveBeenCalled();
});
it('does not reinitialize RiveFile if params do not change', async () => {
const params = {
src: 'file-src',
enableRiveAssetCDN: false
};
const { rerender } = renderHook(() => useRiveFile(params));
rerender();
expect(RiveFile).toHaveBeenCalledTimes(1);
});
it('reinitializes RiveFile if src changes', async () => {
let params = {
src: 'file-src',
enableRiveAssetCDN: false
};
const { rerender } = renderHook(() => useRiveFile(params));
params = {
src: 'new-file-src',
enableRiveAssetCDN: false
};
rerender();
expect(RiveFile).toHaveBeenCalledTimes(2);
});
it('reinitializes RiveFile if buffer changes', async () => {
let params = {
buffer: new ArrayBuffer(10),
enableRiveAssetCDN: false
};
const { rerender } = renderHook(() => useRiveFile(params));
params = {
buffer: new ArrayBuffer(20),
enableRiveAssetCDN: false
};
rerender();
expect(RiveFile).toHaveBeenCalledTimes(2);
});
});