Skip to content

Commit

Permalink
update useHttpClient with custom errorHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
kilbot committed Jul 19, 2023
1 parent 8828762 commit d0c9dc1
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 155 deletions.
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@wcpos/hooks",
"version": "1.3.0",
"version": "1.3.1",
"description": "Hooks for WooCommerce POS",
"author": "kilbot <[email protected]>",
"homepage": "https://github.com/wcpos/hooks#readme",
Expand Down Expand Up @@ -29,29 +29,29 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hotkeys-hook": "^4.4.1",
"react-native": "0.72.1",
"react-native": "0.72.3",
"react-native-gesture-handler": "^2.12.0",
"react-native-reanimated": "3.3.0",
"react-native-web": "~0.19.6",
"rxjs": "7.8.1"
},
"devDependencies": {
"@babel/core": "^7.22.8",
"@storybook/addon-actions": "7.0.26",
"@storybook/addon-controls": "7.0.26",
"@storybook/addon-essentials": "7.0.26",
"@babel/core": "^7.22.9",
"@storybook/addon-actions": "7.1.0",
"@storybook/addon-controls": "7.1.0",
"@storybook/addon-essentials": "7.1.0",
"@storybook/addon-knobs": "7.0.2",
"@storybook/addon-react-native-web": "0.0.20",
"@storybook/builder-webpack5": "7.0.26",
"@storybook/addon-react-native-web": "0.0.21",
"@storybook/builder-webpack5": "7.1.0",
"@storybook/manager-webpack5": "6.5.16",
"@storybook/preset-create-react-app": "7.0.26",
"@storybook/react": "7.0.26",
"@testing-library/jest-dom": "5.16.5",
"@storybook/preset-create-react-app": "7.1.0",
"@storybook/react": "7.1.0",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "^14.0.0",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "^29.5.2",
"@types/jest": "^29.5.3",
"@types/lodash": "4.14.195",
"@types/react": "^18.2.14",
"@types/react": "^18.2.15",
"@wcpos/eslint-config": "*",
"@wcpos/tsconfig": "*",
"babel-loader": "9.1.3",
Expand Down
1 change: 1 addition & 0 deletions src/use-http-client/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { useHttpClient as default } from './use-http-client';
export type { RequestConfig } from './use-http-client';
175 changes: 34 additions & 141 deletions src/use-http-client/use-http-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import * as React from 'react';
import merge from 'lodash/merge';
import set from 'lodash/set';

import useWhyDidYouUpdate from '@wcpos/hooks/src/use-why-did-you-update';
import log from '@wcpos/utils/src/logger';

import http from './http';
Expand All @@ -13,188 +12,82 @@ import useOnlineStatus from '../use-online-status';
type AxiosRequestConfig = import('axios').AxiosRequestConfig;
type AxiosError = import('axios').AxiosError;

export type RequestConfig = AxiosRequestConfig;

interface HttpClientOptions {
errorHandler?: (error: unknown) => unknown;
}

/**
* Http Client provides a standard API for all platforms
* also wraps request to provide a common error handler
*
* NOTE: this hook makes axios.create unnecessary
* - axios.create is synchronous for web, but async for electron
* - axios.create creates more pain than it's worth
*
* TODO - how best to cancel requests
*/
export const useHttpClient = () => {
const errorResponseHandler = useHttpErrorHandler();
export const useHttpClient = (options?: HttpClientOptions) => {
const defaultErrorHandler = useHttpErrorHandler();
const { isInternetReachable } = useOnlineStatus();
// const controller = React.useMemo(() => new AbortController(), []);

/**
* Abort the current request on unmount
*/
// React.useEffect(() => {
// return () => {
// controller.abort();
// };
// }, [controller]);
const retryDelay = 10000; // 10 second

/**
*
*/
const httpWrapper = React.useMemo(() => {
let instanceConfig = {};
const retryCount = 0;
const instanceConfig = {}; // Set default config here

/**
* TODO - merge config with default?
*/
const request = async (config: AxiosRequestConfig = {}) => {
const _config = merge({}, instanceConfig, config); // NOTE: do not mutate instanceConfig
const _config = merge({}, instanceConfig, config);

/**
* Add X-WCPOS header to every request
*/
if (_config.method?.toLowerCase() !== 'head') {
set(_config, ['headers', 'X-WCPOS'], 1);
}

/**
* HTTP HEAD Requests
*
* 1. Set decompress to false
* Fix a bug in windows - see https://github.com/axios/axios/issues/1658
* I get an "unexpected end of file" error on HEAD requests because body is empty
*
* 2. Remove Cookie (probably not needed)
*
* 3. Set query param http_method to HEAD
* Some servers convert HEAD requests to GET requests, eg: WPengine
* I don't know why, but it causes problems with CORS settings in the PHP plugin
*/
if (_config.method?.toLowerCase() === 'head') {
set(_config, 'decompress', false);
// set(_config, ['headers', 'Cookie'], undefined);
/**
* WordPress REST API check for:
* HTTP_X_HTTP_METHOD_OVERRIDE (header)
* _method (get param)
* TODO - check if I should use this instead
*/
set(_config, ['params', '_method'], 'HEAD');
}

/**
* XDEBUG for development
*/
if (process.env.NODE_ENV === 'development') {
set(_config, ['params', 'XDEBUG_SESSION'], 'start');
}

/**
* TODO - do we really need a retry? perhaps better to fail fast and handle the error
*/
try {
const response = await http.request(_config);
// retryCount = 0;
return response;
} catch (error) {
log.error(error);
errorResponseHandler(error as AxiosError);
// retryCount++;
// return new Promise((resolve) => setTimeout(resolve, retryDelay * Math.pow(2, retryCount)));
/**
* Run custom error handler first, then passthrough to default
* This allows us to override the default error handler, eg: detecting 401 and showing login modal
*/
let err = error;
if (typeof options?.errorHandler === 'function') {
err = options.errorHandler(err);
}
if (err) {
defaultErrorHandler(err);
}
}
};

/**
* API
*/
const api = {
axios: http, // expose axios instance, this won't work for electron

/**
*
*/
return {
request,
get(url: string, config: AxiosRequestConfig = {}) {
return request({
...config,
method: 'GET',
url,
data: (config || {}).data,
});
return request({ ...config, method: 'GET', url });
},

/**
*
*/
post(url: string, config: AxiosRequestConfig = {}) {
return request({
...config,
method: 'POST',
url,
data: (config || {}).data,
});
post(url: string, data: any, config: AxiosRequestConfig = {}) {
return request({ ...config, method: 'POST', url, data });
},

put(url: string, config: AxiosRequestConfig = {}) {
return request({
...config,
method: 'PUT',
url,
data: (config || {}).data,
});
put(url: string, data: any, config: AxiosRequestConfig = {}) {
return request({ ...config, method: 'PUT', url, data });
},

/**
*
*/
patch(url: string, config: AxiosRequestConfig = {}) {
return request({
...config,
method: 'PATCH',
url,
data: (config || {}).data,
});
patch(url: string, data: any, config: AxiosRequestConfig = {}) {
return request({ ...config, method: 'PATCH', url, data });
},

/**
*
*/
del(url: string, config: AxiosRequestConfig = {}) {
return request({
...config,
method: 'DELETE',
url,
data: (config || {}).data,
});
delete(url: string, config: AxiosRequestConfig = {}) {
return request({ ...config, method: 'DELETE', url });
},

/**
*
*/
head(url: string, config: AxiosRequestConfig = {}) {
return request({
...config,
method: 'HEAD',
url,
data: (config || {}).data,
});
},
};

/**
* Exposed API
*/
return {
create: (config: AxiosRequestConfig = {}) => {
instanceConfig = config;
return api;
return request({ ...config, method: 'HEAD', url });
},
...api,
};
}, [errorResponseHandler]);
}, [defaultErrorHandler, options]);

/**
*
*/
return httpWrapper;
};
2 changes: 1 addition & 1 deletion src/use-http-client/use-http-error-handler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const useHttpErrorHandler = () => {
*
*/
const errorHandler = React.useCallback(
(error: AxiosError | Error | undefined) => {
(error: unknown) => {
const response = get(error, 'response');
const request = get(error, 'request');

Expand Down

0 comments on commit d0c9dc1

Please sign in to comment.