mirror of
https://github.com/streamich/react-use.git
synced 2026-01-18 14:06:52 +00:00
Add useSpring tests
This commit is contained in:
parent
cecd1adcfa
commit
3c43c4b05d
@ -1,6 +1,6 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import * as React from 'react';
|
||||
import { useSpring } from '..';
|
||||
import useSpring from '../useSpring';
|
||||
import ShowDocs from './util/ShowDocs';
|
||||
|
||||
const Demo = () => {
|
||||
|
||||
119
src/__tests__/useSpring.test.ts
Normal file
119
src/__tests__/useSpring.test.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import useSpring from '../useSpring';
|
||||
import { Spring } from 'rebound';
|
||||
|
||||
// simulate Spring for testing
|
||||
const mockSetCurrentValue = jest.fn();
|
||||
const mockAddListener = jest.fn();
|
||||
const mockSetEndValue = jest.fn();
|
||||
const mockRemoveListener = jest.fn();
|
||||
let triggerSpringUpdate = () => {};
|
||||
let springListener: Listener = { onSpringUpdate: () => {} };
|
||||
|
||||
interface Listener {
|
||||
onSpringUpdate: (currentSpring: Spring) => void;
|
||||
}
|
||||
|
||||
const mockCreateSpring: Spring = jest.fn().mockImplementation(() => {
|
||||
let currentValue = 0;
|
||||
let endValue = 0;
|
||||
|
||||
const getCloserValue = (a, b) => Math.round((a + b) / 2);
|
||||
|
||||
const getCurrentValue = () => {
|
||||
currentValue = getCloserValue(currentValue, endValue);
|
||||
return currentValue;
|
||||
};
|
||||
|
||||
triggerSpringUpdate = () => {
|
||||
if (currentValue !== endValue) {
|
||||
springListener.onSpringUpdate({ getCurrentValue } as any);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
setCurrentValue: val => {
|
||||
currentValue = val;
|
||||
mockSetCurrentValue(val);
|
||||
},
|
||||
addListener: newListener => {
|
||||
springListener = newListener;
|
||||
mockAddListener(newListener);
|
||||
},
|
||||
setEndValue: val => {
|
||||
endValue = val;
|
||||
mockSetEndValue(val);
|
||||
},
|
||||
removeListener: mockRemoveListener,
|
||||
};
|
||||
}) as any;
|
||||
|
||||
jest.mock('rebound', () => {
|
||||
return {
|
||||
Sprint: {},
|
||||
SpringSystem: jest.fn().mockImplementation(() => {
|
||||
return { createSpring: mockCreateSpring };
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
it('should init value to provided target', () => {
|
||||
const { result } = renderHook(() => useSpring(70));
|
||||
|
||||
expect(result.current).toBe(70);
|
||||
expect(mockSetCurrentValue).toHaveBeenCalledTimes(1);
|
||||
expect(mockSetCurrentValue).toHaveBeenCalledWith(70);
|
||||
expect(mockCreateSpring).toHaveBeenCalledTimes(1);
|
||||
expect(mockCreateSpring).toHaveBeenCalledWith(50, 3);
|
||||
});
|
||||
|
||||
it('should create spring with custom tension and friction args provided', () => {
|
||||
renderHook(() => useSpring(500, 20, 7));
|
||||
|
||||
expect(mockCreateSpring).toHaveBeenCalledTimes(1);
|
||||
expect(mockCreateSpring).toHaveBeenCalledWith(20, 7);
|
||||
});
|
||||
|
||||
it('should subscribe only once', () => {
|
||||
const { rerender } = renderHook(() => useSpring());
|
||||
|
||||
expect(mockAddListener).toHaveBeenCalledTimes(1);
|
||||
expect(mockAddListener).toHaveBeenCalledWith(springListener);
|
||||
|
||||
rerender();
|
||||
|
||||
expect(mockAddListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle spring update', () => {
|
||||
let targetValue = 70;
|
||||
let lastSpringValue = targetValue;
|
||||
const { result, rerender } = renderHook(() => useSpring(targetValue));
|
||||
|
||||
targetValue = 100;
|
||||
rerender();
|
||||
expect(result.current).toBe(lastSpringValue);
|
||||
|
||||
act(() => {
|
||||
triggerSpringUpdate(); // simulate new spring value
|
||||
});
|
||||
expect(result.current).toBeGreaterThan(lastSpringValue);
|
||||
expect(result.current).toBeLessThanOrEqual(targetValue);
|
||||
|
||||
lastSpringValue = result.current;
|
||||
act(() => {
|
||||
triggerSpringUpdate(); // simulate another new spring value
|
||||
});
|
||||
expect(result.current).toBeGreaterThan(lastSpringValue);
|
||||
expect(result.current).toBeLessThanOrEqual(targetValue);
|
||||
});
|
||||
|
||||
it('should remove listener on unmount', () => {
|
||||
const { unmount } = renderHook(() => useSpring());
|
||||
expect(mockRemoveListener).not.toHaveBeenCalled();
|
||||
|
||||
unmount();
|
||||
|
||||
expect(mockRemoveListener).toHaveBeenCalledTimes(1);
|
||||
expect(mockRemoveListener).toHaveBeenCalledWith(springListener);
|
||||
});
|
||||
@ -1,31 +1,37 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { Spring, SpringSystem } from 'rebound';
|
||||
|
||||
const useSpring = (targetValue: number = 0, tension: number = 50, friction: number = 3) => {
|
||||
const [spring, setSpring] = useState<Spring | null>(null);
|
||||
const [value, setValue] = useState<number>(targetValue);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = {
|
||||
// memoize listener to being able to unsubscribe later properly, otherwise
|
||||
// listener fn will be different on each re-render and wouldn't unsubscribe properly.
|
||||
const listener = useMemo(
|
||||
() => ({
|
||||
onSpringUpdate: currentSpring => {
|
||||
const newValue = currentSpring.getCurrentValue();
|
||||
setValue(newValue);
|
||||
},
|
||||
};
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!spring) {
|
||||
const newSpring = new SpringSystem().createSpring(tension, friction);
|
||||
newSpring.setCurrentValue(targetValue);
|
||||
setSpring(newSpring);
|
||||
newSpring.addListener(listener);
|
||||
return;
|
||||
}
|
||||
|
||||
return () => {
|
||||
spring.removeListener(listener);
|
||||
setSpring(null);
|
||||
if (spring) {
|
||||
spring.removeListener(listener);
|
||||
setSpring(null);
|
||||
}
|
||||
};
|
||||
}, [tension, friction]);
|
||||
}, [tension, friction, spring]);
|
||||
|
||||
useEffect(() => {
|
||||
if (spring) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user