React + HTTP = ReHttp
A highly customizable http client library for react
Features
- [x] No bloated dependencies
- [x] Optional global configuration
- [x] Typescript support
- [x] Promise based
- [x] Hooks for function component
- [x] Lazy fetch support
- [x] Transforming request and response
- [x] Callbacks for request, response and error
- [x] Caching
- [x] Use outside of react context
- [x] Component (for class based component)
Installation
Yarn:
yarn add @harryy/rehttp
NPM:
npm install @harryy/rehttp
Usage
import React from 'react'
import { useReHttp } from 'rehttp'
const Example: React.FC = () => {
const {data, refetch, loading} = useReHttp({
url: 'https://jsonplaceholder.typicode.com/posts/1'
})
if (loading) {
return <>Loading...</>
}
return (
<>
{data.title}
<button onClick={() => refetch()}>Refetch</button>
</>
)
}
API
const {data, response, error, loading, execute, isRequestInFlight, cached} = useReHttp(request, options?)
By default, request will be fired if lazy: true
is not passed as an option.
data
contains json serialized data
response
contains the raw response from the server
error
contains the error if any
loading
is true
only when there is no data or error
isRequestInFlight
tells whether the request is in flight or not
execute
can be called with optional request
parameters to refetch data again
cached
gives the cached object if the request was cached earlier
import React from 'react'
import { useReHttp, ReHttpRequest, ReHttpResponse } from 'rehttp'
interface Post {
id: string,
title: string,
}
interface PostError {
message: string,
status: 'NOTOK',
}
useReHttp<Post, PostError>({
method: 'GET', // Optional, type: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT', default: 'GET'
url: 'https://jsonplaceholder.typicode.com/posts', // Optional, type: string, default: ''
headers: {
Accept: 'application/json'
}, // Optional, type: Record<string, string>, default: undefined
params: {
page: 1,
per_page: 5,
tags: ['hello', 'world']
}, // Optional, type: Record<string, string | number | Array<string | number>>, default: undefined
body: undefined // Optional, type: any, default: undefined
}, {
transformError: async (e) => {
return {
status: 'NOTOK',
message: e.message
}
}, // Optional, type: (data: any) => Promise<PostError>, default: undefined
transformResponse: async (data: any, response: ReHttpResponse) => {
if (data.id && response.status === 200) {
return {
id: data.id,
title: data.title.toUpperCase()
}
} else {
return Promise.reject(new Error('Post has no id'))
}
}, // Optional, type: (data: any, response: ReHttpResponse) => Promise<Post>, default: undefined
transformRequest: async (res: ReHttpRequest) => {
const token = await SomeAsyncStorageOrApiOrWhatever().token
return {
...res,
params: {
foo: 'bar',
},
headers: {
Authorization: `Bearer ${token}`
}
}
}, // Optional, type: (data: ReHttpRequest) => Promise<ReHttpRequest>, default: undefined
lazy: true, // Optional, type: boolean, default: false
noCache: true, // Optional, type: boolean, default: false
});
Need to use in class component? No Problem!
import React from 'react'
import { ReHttp, ReHttpProps } from 'rehttp'
interface Post {
id: string,
title: string,
}
interface PostError {
message: string,
status: 'NOTOK',
}
class MyComponent extends React.Component {
reHttpProps: Omit<ReHttpProps<Post, PostError>, 'children'> = {
method: 'GET', // Optional, type: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT', default: 'GET'
url: 'https://jsonplaceholder.typicode.com/posts', // Optional, type: string, default: ''
headers: {
Accept: 'application/json'
}, // Optional, type: Record<string, string>, default: { Accept: 'application/json', 'Content-Type': 'application/json' }
params: {
page: 1,
per_page: 5,
tags: ['hello', 'world']
}, // Optional, type: Record<string, string | number | Array<string | number>>, default: undefined
body: undefined, // Optional, type: any, default: undefined
onRequest: () => {
showLoader()
}, //Optional, type: (data: ReHttpRequest) => Promise<void>
onResponse: () => {
showSuccess('request success')
}, //Optional, type: (data: Post, response: ReHttpResponse) => Promise<void>
onError: (error) => {
showAlert(error.message)
}, // Optional, type: (error: PostError) => Promise<void>
transformError: async (e) => {
return {
status: 'NOTOK',
message: e.message
}
}, // Optional, type: (data: any) => Promise<PostError>, default: undefined
transformResponse: async (data: any, response: ReHttpResponse) => {
if (data.id && response.status === 200) {
return {
id: data.id,
title: data.title.toUpperCase()
}
} else {
return Promise.reject(new Error('Post has no id'))
}
}, // Optional, type: (data: any, response: ReHttpResponse) => Promise<Post>, default: undefined
transformRequest: async (res: ReHttpRequest) => {
const token = await SomeAsyncStorageOrApiOrWhatever().token
return {
...res,
params: {
foo: 'bar',
},
headers: {
Authorization: `Bearer ${token}`
}
}
}, // Optional, type: (data: ReHttpRequest) => Promise<ReHttpRequest>, default: undefined
lazy: true, // Optional, type: boolean, default: false
noCache: true, // Optional, type: boolean, default: false
}
render() {
<ReHttp<Post, PostError> {...this.reHttpProps}>
{
({data, refetch}) => (
<>
{data.title}
<button onClick={() => refetch()}>Refetch</button>
</>
)
</ReHttp>
}
}
Need to set options globally? No problem!
ReHttpProvider
import React from 'react'
import { ReHttpProvider, ReHttpProviderProps, ReHttpRequest, ReHttpResponse, InMemoryAdapter } from 'rehttp'
const App: React.FC = () => {
const options: ReHttpProviderProps = {
cacheAdapter: new InMemoryAdapter({ttl: 5 * 60 * 1000, size: 50}), // Optional, type: CacheAdapter, params: {ttl?: 5 * 60* 1000, size?: 50}?, ttl is in milliseconds
cacheMethods: ['GET'], // Optional, type: Array<ReHttpRequest['method']>, default: ['GET'], http methods that needs to be cached
baseUrl: 'https://jsonplaceholder.typicode.com', // Optional, type: string
method: 'GET', // Optional, type: 'DELETE' | 'GET' | 'PATCH' | 'POST' | 'PUT'
params: {}, // Optional, type: Record<string, string | number | Array<string | number>>
headers: {}, // Optional, type: Record<string, string>
onRequest: () => {
showLoader()
}, //Optional, type: (data: ReHttpRequest) => Promise<void>
onResponse: () => {
showSuccess('request success')
}, //Optional, type: (data: any, response: ReHttpResponse) => Promise<void>
onError: (error) => {
showAlert(error.message)
}, // Optional, type: (error: any) => Promise<void>
onComplete: (dataOrError, response) => {
hideLoader()
}, // Optional, type: (dataOrError: any, response?: ReHttpResponse) => Promise<void>
transformError: async (e) => {
return {
status: 'NOTOK',
message: e.message
}
}, // Optional, type: (data: any) => Promise<PostError>, default: undefined
transformResponse: async (data: any, response: ReHttpResponse) => {
if (data.id && response.status === 200) {
return {
id: data.id,
title: data.title.toUpperCase()
}
} else {
return Promise.reject(new Error('Post has no id'))
}
}, // Optional, type: (data: any, response: ReHttpResponse) => Promise<Post>, default: undefined
transformRequest: async (res: ReHttpRequest) => {
const token = await SomeAsyncStorageOrApiOrWhatever().token
return {
...res,
params: {
foo: 'bar',
},
headers: {
Authorization: `Bearer ${token}`
}
}
}, // Optional, type: (data: ReHttpRequest) => Promise<ReHttpRequest>, default: undefined
lazy: true, // Optional, type: boolean
}
return (
<ReHttpProvider {...options}>
<Main />
</ReHttpProvider>
)
}
Need to use it outside of react context (like redux thunk etc.) and still use context values? No Problem!
reHttpInstance
import React from 'react'
import { reHttpInstance } from 'rehttp'
reHttpInstance({
url: 'https://jsonplaceholder.typicode.com'
}).then(res => {
console.log('Response from ReHttp', res)
}) // option lazy: true will have no effect here
const MyComponent: React.FC = () => {
return <div>Hello World!</div>
}
License
MIT © harryy2510