Skip to content

Commit

Permalink
React 18, types, and bumped testing library things
Browse files Browse the repository at this point in the history
  • Loading branch information
imjordanxd committed Feb 7, 2025
1 parent ad33f76 commit 5f3b9ce
Show file tree
Hide file tree
Showing 77 changed files with 926 additions and 883 deletions.
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"set-harmonic-interval": "^1.0.1",
"throttle-debounce": "^3.0.1",
"ts-easing": "^0.2.0",
"tslib": "^2.1.0"
"tslib": "^2.8.1"
},
"peerDependencies": {
"react": "*",
Expand All @@ -81,10 +81,11 @@
"@storybook/addon-notes": "5.3.21",
"@storybook/addon-options": "5.3.21",
"@storybook/react": "6.4.9",
"@testing-library/react": "12.1.2",
"@testing-library/react-hooks": "7.0.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "16.2.0",
"@types/jest": "27.5.2",
"@types/react": "17.0.0",
"@types/react": "18.3.18",
"@types/react-dom": "18.3.5",
"@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "5.62.0",
"babel-core": "6.26.3",
Expand All @@ -110,11 +111,11 @@
"markdown-loader": "6.0.0",
"prettier": "2.3.0",
"raf-stub": "3.0.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-frame-component": "5.2.7",
"react-spring": "9.7.4",
"react-test-renderer": "17.0.2",
"react-test-renderer": "18.3.1",
"rebound": "0.1.0",
"redux-logger": "3.0.6",
"redux-thunk": "2.4.2",
Expand Down Expand Up @@ -164,4 +165,4 @@
"type": "opencollective",
"url": "https://opencollective.com/react-use"
}
}
}
9 changes: 4 additions & 5 deletions src/useEnsuredForwardedRef.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {
forwardRef,
ForwardRefExoticComponent,
ForwardRefRenderFunction,
MutableRefObject,
PropsWithChildren,
PropsWithoutRef,
RefAttributes,
RefForwardingComponent,
useEffect,
useRef,
} from 'react';
Expand All @@ -26,10 +25,10 @@ export default function useEnsuredForwardedRef<T>(
}

export function ensuredForwardRef<T, P = {}>(
Component: RefForwardingComponent<T, P>
Component: ForwardRefRenderFunction<T, P>
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> {
return forwardRef((props: PropsWithChildren<P>, ref) => {
return forwardRef((props: PropsWithoutRef<P>, ref) => {
const ensuredRef = useEnsuredForwardedRef(ref as MutableRefObject<T>);
return Component(props, ensuredRef);
return Component(props as P, ensuredRef);
});
}
1 change: 1 addition & 0 deletions src/useMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const getInitialState = (query: string, defaultState?: boolean) => {
};

const useMedia = (query: string, defaultState?: boolean) => {
// TODO: refactor this to use `React#useSyncExternalStore`
const [state, setState] = useState(getInitialState(query, defaultState));

useEffect(() => {
Expand Down
2 changes: 1 addition & 1 deletion tests/createBreakpoint.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, renderHook } from '@testing-library/react-hooks';
import { act, renderHook } from '@testing-library/react';
import createBreakpoint from '../src/factory/createBreakpoint';

const useBreakpointA = createBreakpoint();
Expand Down
2 changes: 1 addition & 1 deletion tests/createGlobalState.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, renderHook } from '@testing-library/react-hooks';
import { act, renderHook } from '@testing-library/react';
import createGlobalState from '../src/factory/createGlobalState';

describe('useGlobalState', () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/createMemo.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { renderHook } from '@testing-library/react-hooks';
import { renderHook } from '@testing-library/react';
import createMemo from '../src/factory/createMemo';

const getDouble = jest.fn((n: number): number => n * 2);
Expand Down
2 changes: 1 addition & 1 deletion tests/createReducer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, renderHook } from '@testing-library/react-hooks';
import { act, renderHook } from '@testing-library/react';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import createReducer from '../src/factory/createReducer';
Expand Down
16 changes: 11 additions & 5 deletions tests/createReducerContext.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { act, renderHook } from '@testing-library/react-hooks';
import { act, renderHook } from '@testing-library/react';
import createReducerContext from '../src/factory/createReducerContext';

type Action = 'increment' | 'decrement';
Expand All @@ -25,10 +25,16 @@ it('should create a hook and a provider', () => {
describe('when using created hook', () => {
it('should throw out of a provider', () => {
const [useSharedNumber] = createReducerContext(reducer, 0);
const { result } = renderHook(() => useSharedNumber());
expect(result.error).toEqual(
new Error('useReducerContext must be used inside a ReducerProvider.')
);
let error: Error | undefined;
renderHook(() => useSharedNumber(), {
wrapper: class extends React.Component<React.PropsWithChildren> {
componentDidCatch(err) {
error = err;
}
render = () => this.props.children;
},
});
expect(error).toEqual(new Error('useReducerContext must be used inside a ReducerProvider.'));
});

const setUp = () => {
Expand Down
14 changes: 11 additions & 3 deletions tests/createStateContext.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { act, renderHook } from '@testing-library/react-hooks';
import { act, renderHook } from '@testing-library/react';
import createStateContext from '../src/factory/createStateContext';

it('should create a hook and a provider', () => {
Expand All @@ -12,8 +12,16 @@ it('should create a hook and a provider', () => {
describe('when using created hook', () => {
it('should throw out of a provider', () => {
const [useSharedText] = createStateContext('init');
const { result } = renderHook(() => useSharedText());
expect(result.error).toEqual(new Error('useStateContext must be used inside a StateProvider.'));
let error: Error | undefined;
renderHook(() => useSharedText(), {
wrapper: class extends React.Component<{ children: React.ReactNode }> {
componentDidCatch(err: Error) {
error = err;
}
render = () => this.props.children;
},
});
expect(error).toEqual(new Error('useStateContext must be used inside a StateProvider.'));
});

const setUp = () => {
Expand Down
63 changes: 32 additions & 31 deletions tests/useAsync.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { renderHook } from '@testing-library/react-hooks';
import { renderHook, RenderHookResult, waitFor } from '@testing-library/react';
import { useCallback } from 'react';
import useAsync from '../src/useAsync';

Expand All @@ -8,11 +8,11 @@ describe('useAsync', () => {
});

describe('a success', () => {
let hook;
let hook: RenderHookResult<ReturnType<typeof useAsync>, { fn: () => Promise<string> }>;
let callCount = 0;

const resolver = async () => {
return new Promise((resolve) => {
return new Promise<string>((resolve) => {
callCount++;

const wait = setTimeout(() => {
Expand All @@ -33,28 +33,26 @@ describe('useAsync', () => {

it('initially starts loading', async () => {
expect(hook.result.current.loading).toEqual(true);
await hook.waitForNextUpdate();
});

it('resolves', async () => {
expect.assertions(4);

hook.rerender({ fn: resolver });
await hook.waitForNextUpdate();
await waitFor(() => {
expect(callCount).toEqual(1);
expect(hook.result.current.loading).toBeFalsy();
});

expect(callCount).toEqual(1);
expect(hook.result.current.loading).toBeFalsy();
expect(hook.result.current.value).toEqual('yay');
expect(hook.result.current.error).toEqual(undefined);
});
});

describe('an error', () => {
let hook;
let hook: RenderHookResult<ReturnType<typeof useAsync>, { fn: () => Promise<never> }>;
let callCount = 0;

const rejection = async () => {
return new Promise((_, reject) => {
return new Promise<never>((_, reject) => {
callCount++;

const wait = setTimeout(() => {
Expand All @@ -75,25 +73,23 @@ describe('useAsync', () => {

it('initially starts loading', async () => {
expect(hook.result.current.loading).toBeTruthy();
await hook.waitForNextUpdate();
});

it('resolves', async () => {
expect.assertions(4);

hook.rerender({ fn: rejection });
await hook.waitForNextUpdate();
await waitFor(() => {
expect(callCount).toEqual(1);
expect(hook.result.current.loading).toBeFalsy();
});

expect(callCount).toEqual(1);
expect(hook.result.current.loading).toBeFalsy();
expect(hook.result.current.error).toEqual('yay');
expect(hook.result.current.value).toEqual(undefined);
});
});

describe('re-evaluates when dependencies change', () => {
describe('the fn is a dependency', () => {
let hook;
let hook: RenderHookResult<ReturnType<typeof useAsync>, { fn: () => Promise<string> }>;
let callCount = 0;

const initialFn = async () => {
Expand All @@ -106,43 +102,47 @@ describe('useAsync', () => {
return 'new value';
};

beforeEach((done) => {
beforeEach(async () => {
callCount = 0;

hook = renderHook(({ fn }) => useAsync(fn, [fn]), {
initialProps: { fn: initialFn },
});

hook.waitForNextUpdate().then(done);
await waitFor(() => {
expect(callCount).toEqual(1);
});
});

it('renders the first value', () => {
expect(hook.result.current.value).toEqual('value');
});

it('renders a different value when deps change', async () => {
expect.assertions(3);

expect(callCount).toEqual(1);

hook.rerender({ fn: differentFn }); // change the fn to initiate new request
await hook.waitForNextUpdate();
await waitFor(() => {
expect(callCount).toEqual(2);
});

expect(callCount).toEqual(2);
expect(hook.result.current.value).toEqual('new value');
});
});

describe('the additional dependencies list changes', () => {
let callCount = 0;
let hook;
let hook: RenderHookResult<
ReturnType<typeof useAsync>,
{ fn: (counter: number) => Promise<string>; counter: number }
>;

const staticFunction = async (counter) => {
callCount++;
return `counter is ${counter} and callCount is ${callCount}`;
};

beforeEach((done) => {
beforeEach(async () => {
callCount = 0;
hook = renderHook(
({ fn, counter }) => {
Expand All @@ -156,19 +156,20 @@ describe('useAsync', () => {
},
}
);

hook.waitForNextUpdate().then(done);
await waitFor(() => {
expect(callCount).toEqual(1);
});
});

it('initial renders the first passed pargs', () => {
expect(hook.result.current.value).toEqual('counter is 0 and callCount is 1');
});

it('renders a different value when deps change', async () => {
expect.assertions(1);

hook.rerender({ fn: staticFunction, counter: 1 });
await hook.waitForNextUpdate();
await waitFor(() => {
expect(callCount).toEqual(2);
});

expect(hook.result.current.value).toEqual('counter is 1 and callCount is 2');
});
Expand Down
Loading

0 comments on commit 5f3b9ce

Please sign in to comment.