[NEW] Create useEndpointData for call endpoints using hooks (#4342)
* create basic useEndpointData for call endpoints using hooks * remove cache * create base for useEndpointData test * create basic useEndpointData for call endpoints using hooks * remove cache * create base for useEndpointData test * fix preset * update tests * change order * create ErrorResult and add error to return * update tests
This commit is contained in:
parent
2b08b683d7
commit
89820b2bec
|
@ -91,3 +91,5 @@ export type ResultFor<TMethod extends Method, TPathPattern extends PathPattern>
|
||||||
| SuccessResult<OperationResult<TMethod, TPathPattern>>
|
| SuccessResult<OperationResult<TMethod, TPathPattern>>
|
||||||
| FailureResult<unknown, unknown, unknown, unknown>
|
| FailureResult<unknown, unknown, unknown, unknown>
|
||||||
| UnauthorizedResult<unknown>;
|
| UnauthorizedResult<unknown>;
|
||||||
|
|
||||||
|
export type ErrorResult = FailureResult<unknown, unknown, unknown, unknown>;
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { renderHook } from '@testing-library/react-hooks';
|
||||||
|
import { render, waitFor } from '@testing-library/react-native';
|
||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
|
||||||
|
import { useEndpointData } from './useEndpointData';
|
||||||
|
import sdk from '../services/sdk';
|
||||||
|
|
||||||
|
const url = 'chat.getMessage';
|
||||||
|
|
||||||
|
export const message = {
|
||||||
|
_id: '9tYkmJ67wMwmvQouD',
|
||||||
|
t: 'uj',
|
||||||
|
rid: 'GENERAL',
|
||||||
|
ts: '2022-07-05T19:34:30.146Z',
|
||||||
|
msg: 'xdani',
|
||||||
|
u: {
|
||||||
|
_id: 'ombax8oEZnE7N3Mtt',
|
||||||
|
username: 'xdani',
|
||||||
|
name: 'xdani'
|
||||||
|
},
|
||||||
|
groupable: false,
|
||||||
|
_updatedAt: '2022-07-05T19:34:30.146Z'
|
||||||
|
};
|
||||||
|
|
||||||
|
// mock sdk
|
||||||
|
jest.mock('../services/sdk', () => ({
|
||||||
|
get: jest.fn(() => new Promise(resolve => setTimeout(() => resolve({ success: true, message }), 1000)))
|
||||||
|
}));
|
||||||
|
|
||||||
|
function Render() {
|
||||||
|
const { loading } = useEndpointData(url, { msgId: message._id });
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text testID='loading'>loading</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text testID='load complete'>load complete</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('useFetch', () => {
|
||||||
|
it('should return data after fetch', async () => {
|
||||||
|
const { result, waitForNextUpdate } = renderHook(() => useEndpointData(url, { msgId: message._id }));
|
||||||
|
expect(result.current.loading).toEqual(true);
|
||||||
|
expect(result.current.result).toEqual(undefined);
|
||||||
|
await waitForNextUpdate();
|
||||||
|
expect(result.current.loading).toEqual(false);
|
||||||
|
expect(result.current.result).toEqual({ success: true, message });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should component load correctly', async () => {
|
||||||
|
const renderComponent = render(<Render />);
|
||||||
|
const loading = await renderComponent.findByTestId('loading');
|
||||||
|
expect(loading.props.children).toBe('loading');
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
expect(renderComponent.getByText('load complete')).toBeTruthy();
|
||||||
|
},
|
||||||
|
{ timeout: 2000 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return error after fetch', async () => {
|
||||||
|
const spy = jest
|
||||||
|
.spyOn(sdk, 'get')
|
||||||
|
.mockImplementation(
|
||||||
|
jest.fn(() => new Promise(resolve => setTimeout(() => resolve({ success: false, error: null }), 1000)))
|
||||||
|
);
|
||||||
|
|
||||||
|
const { result, waitForNextUpdate } = renderHook(() => useEndpointData(url, { msgId: message._id }));
|
||||||
|
expect(result.current.loading).toEqual(true);
|
||||||
|
expect(result.current.result).toEqual(undefined);
|
||||||
|
expect(result.current.error).toEqual(undefined);
|
||||||
|
await waitForNextUpdate();
|
||||||
|
expect(result.current.loading).toEqual(false);
|
||||||
|
expect(result.current.result).toEqual(undefined);
|
||||||
|
expect(result.current.error).toEqual({ success: false, error: null });
|
||||||
|
|
||||||
|
spy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,62 @@
|
||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { ErrorResult, MatchPathPattern, OperationParams, PathFor, ResultFor, Serialized } from '../../definitions/rest/helpers';
|
||||||
|
import sdk from '../services/sdk';
|
||||||
|
|
||||||
|
export const useEndpointData = <TPath extends PathFor<'GET'>>(
|
||||||
|
endpoint: TPath,
|
||||||
|
params: void extends OperationParams<'GET', MatchPathPattern<TPath>>
|
||||||
|
? void
|
||||||
|
: Serialized<OperationParams<'GET', MatchPathPattern<TPath>>> = undefined as void extends OperationParams<
|
||||||
|
'GET',
|
||||||
|
MatchPathPattern<TPath>
|
||||||
|
>
|
||||||
|
? void
|
||||||
|
: Serialized<OperationParams<'GET', MatchPathPattern<TPath>>>
|
||||||
|
): {
|
||||||
|
result: Serialized<ResultFor<'GET', MatchPathPattern<TPath>>> | undefined;
|
||||||
|
loading: boolean;
|
||||||
|
reload: Function;
|
||||||
|
error: ErrorResult | undefined;
|
||||||
|
} => {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [result, setResult] = useState<Serialized<ResultFor<'GET', MatchPathPattern<TPath>>> | undefined>();
|
||||||
|
const [error, setError] = useState<ErrorResult | undefined>();
|
||||||
|
|
||||||
|
const paramsRef = useRef(params);
|
||||||
|
|
||||||
|
if (!isEqual(paramsRef.current, params)) {
|
||||||
|
paramsRef.current = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = useCallback(() => {
|
||||||
|
if (!endpoint) return;
|
||||||
|
setLoading(true);
|
||||||
|
sdk
|
||||||
|
.get(endpoint, params)
|
||||||
|
.then(e => {
|
||||||
|
setLoading(false);
|
||||||
|
if (e.success) {
|
||||||
|
setResult(e);
|
||||||
|
} else {
|
||||||
|
setError(e as ErrorResult);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e: ErrorResult) => {
|
||||||
|
setLoading(false);
|
||||||
|
setError(e);
|
||||||
|
});
|
||||||
|
}, [paramsRef.current]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchData();
|
||||||
|
}, [fetchData]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
loading,
|
||||||
|
reload: fetchData,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
const jestRN = require('react-native/jest-preset');
|
const jestRN = require('@testing-library/react-native/jest-preset/index.js');
|
||||||
const jestExpo = require('jest-expo/jest-preset.js');
|
const jestExpo = require('jest-expo/jest-preset.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -154,6 +154,7 @@
|
||||||
"@storybook/addon-storyshots": "5.3.21",
|
"@storybook/addon-storyshots": "5.3.21",
|
||||||
"@storybook/react-native": "5.3.25",
|
"@storybook/react-native": "5.3.25",
|
||||||
"@testing-library/jest-native": "^4.0.4",
|
"@testing-library/jest-native": "^4.0.4",
|
||||||
|
"@testing-library/react-hooks": "^8.0.1",
|
||||||
"@testing-library/react-native": "^9.0.0",
|
"@testing-library/react-native": "^9.0.0",
|
||||||
"@types/bytebuffer": "^5.0.43",
|
"@types/bytebuffer": "^5.0.43",
|
||||||
"@types/ejson": "^2.1.3",
|
"@types/ejson": "^2.1.3",
|
||||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -5314,6 +5314,14 @@
|
||||||
ramda "^0.26.1"
|
ramda "^0.26.1"
|
||||||
redent "^2.0.0"
|
redent "^2.0.0"
|
||||||
|
|
||||||
|
"@testing-library/react-hooks@^8.0.1":
|
||||||
|
version "8.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
|
||||||
|
integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
react-error-boundary "^3.1.0"
|
||||||
|
|
||||||
"@testing-library/react-native@^9.0.0":
|
"@testing-library/react-native@^9.0.0":
|
||||||
version "9.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@testing-library/react-native/-/react-native-9.0.0.tgz#e9c63411e93d2e8e70d744b12aeb78c58025c5fc"
|
resolved "https://registry.yarnpkg.com/@testing-library/react-native/-/react-native-9.0.0.tgz#e9c63411e93d2e8e70d744b12aeb78c58025c5fc"
|
||||||
|
@ -16424,6 +16432,13 @@ react-draggable@^4.0.3:
|
||||||
classnames "^2.2.5"
|
classnames "^2.2.5"
|
||||||
prop-types "^15.6.0"
|
prop-types "^15.6.0"
|
||||||
|
|
||||||
|
react-error-boundary@^3.1.0:
|
||||||
|
version "3.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
|
||||||
|
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
|
||||||
react-error-overlay@^6.0.3:
|
react-error-overlay@^6.0.3:
|
||||||
version "6.0.9"
|
version "6.0.9"
|
||||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
|
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
|
||||||
|
|
Loading…
Reference in New Issue