Skip to content

Fixes/changes #234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 9 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ Todos
- .json() ['application/json']
- .text() ['text/plain']
- .blob() ['image/png', 'application/octet-stream']
- [ ] is making a [gitpod](https://www.gitpod.io/docs/configuration/) useful here? 🤔
- [ ] suspense
- [ ] triggering it from outside the `<Suspense />` component.
- add `.read()` to `request`
Expand Down Expand Up @@ -997,21 +998,6 @@ Todos
- [ ] show comparison with Apollo
- [ ] figure out a good way to show side-by-side comparisons
- [ ] show comparison with Axios
- [ ] maybe add syntax for middle helpers for inline `headers` or `queries` like this:

```jsx
const request = useFetch('https://example.com')

request
.headers({
auth: jwt // this would inline add the `auth` header
})
.query({ // might have to use .params({ }) since we're using .query() for GraphQL
no: 'way' // this would inline make the url: https://example.com?no=way
})
.get()
```

- [ ] potential option ideas

```jsx
Expand All @@ -1021,6 +1007,14 @@ Todos
// to overwrite those of `useFetch` for
// `useMutation` and `useQuery`
},
responseType: 'json', // similar to axios
// OR can be an array. We will try to get the `data`
// by attempting to extract it via these body interface
// methods, one by one in this order
responseType: ['json', 'text', 'blob', 'formData', 'arrayBuffer'],
// ALSO, maybe there's a way to guess the proper `body interface method` for the correct response content-type.
// here's a stackoverflow with someone who's tried: https://bit.ly/2X8iaVG

// Allows you to pass in your own cache to useFetch
// This is controversial though because `cache` is an option in the requestInit
// and it's value is a string. See: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "use-http",
"version": "0.4.4",
"version": "0.4.5",
"homepage": "http://use-http.com",
"main": "dist/index.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/doFetchArgs.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import doFetchArgs from '../doFetchArgs'
import { HTTPMethod } from '../types'
import { defaults } from '../useFetchArgs'
import defaults from '../defaults'
import useCache from '../useCache'

describe('doFetchArgs: general usages', (): void => {
Expand Down
51 changes: 44 additions & 7 deletions src/__tests__/useFetch.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
/* eslint-disable no-var */
/* eslint-disable camelcase */
/* eslint-disable @typescript-eslint/camelcase */
import React, { ReactElement, ReactNode } from 'react'
import React, { ReactElement, ReactNode, useEffect } from 'react'
import { useFetch, Provider } from '..'
import { cleanup } from '@testing-library/react'
import * as test from '@testing-library/react'
import { FetchMock } from 'jest-fetch-mock'
import { toCamel } from 'convert-keys'
import { renderHook, act } from '@testing-library/react-hooks'
import mockConsole from 'jest-mock-console'
import * as mockdate from 'mockdate'
import defaults from '../defaults'

import { Res, Options, CachePolicies } from '../types'
import { emptyCustomResponse, sleep, makeError } from '../utils'
Expand Down Expand Up @@ -92,11 +94,42 @@ describe('useFetch - BROWSER - basic functionality', (): void => {
var formData = new FormData()
formData.append('username', 'AlexCory')
await result.current.post(formData)
const options = fetch.mock.calls[0][1] || {}
const options = fetch.mock.calls[0][1] || { headers: {} }
expect(options.method).toBe('POST')
expect(options.headers).toBeUndefined()
expect('Content-Type' in (options as any).headers).toBe(false)
})
})

it('should not cause infinite loop with `[request]` as dependency', async () => {
function Section() {
const { request, data } = useFetch('https://a.co')
useEffect(() => {
request.get()
}, [request])
return <div>{JSON.stringify(data)}</div>
}
const { container } = test.render(<Section />)

await test.act(async (): Promise<any> => await sleep(100))
expect(JSON.parse(container.textContent as string)).toEqual(expected)
})

it('should not cause infinite loop with `[response]` as dependency', async () => {
function Section() {
const { request, response, data } = useFetch('https://a.co')
useEffect(() => {
(async () => {
await request.get()
if (!response.ok) console.error('no okay')
})()
}, [request, response])
return <div>{JSON.stringify(data)}</div>
}
const { container } = test.render(<Section />)

await test.act(async (): Promise<any> => await sleep(100))
expect(JSON.parse(container.textContent as string)).toEqual(expected)
})
})

describe('useFetch - BROWSER - with <Provider />', (): void => {
Expand Down Expand Up @@ -591,8 +624,9 @@ describe('useFetch - BROWSER - Overwrite Global Options set in Provider', (): vo
})

it('should only add Content-Type: application/json for POST and PUT by default', async (): Promise<void> => {
const expectedHeadersGET = providerHeaders
const expectedHeadersGET = { ...defaults.headers, ...providerHeaders }
const expectedHeadersPOSTandPUT = {
...defaults.headers,
...providerHeaders,
'Content-Type': 'application/json'
}
Expand All @@ -613,7 +647,10 @@ describe('useFetch - BROWSER - Overwrite Global Options set in Provider', (): vo
})

it('should have the correct headers set in the options set in the Provider', async (): Promise<void> => {
const expectedHeaders = providerHeaders
const expectedHeaders = {
...defaults.headers,
...providerHeaders
}
const { result } = renderHook(
() => useFetch(),
{ wrapper }
Expand All @@ -625,7 +662,7 @@ describe('useFetch - BROWSER - Overwrite Global Options set in Provider', (): vo
})

it('should overwrite url and options set in the Provider', async (): Promise<void> => {
const expectedHeaders = undefined
const expectedHeaders = defaults.headers
const expectedURL = 'https://example2.com'
const { result, waitForNextUpdate } = renderHook(
() => useFetch(expectedURL, globalOptions => {
Expand All @@ -644,7 +681,7 @@ describe('useFetch - BROWSER - Overwrite Global Options set in Provider', (): vo
})

it('should overwrite options set in the Provider', async (): Promise<void> => {
const expectedHeaders = undefined
const expectedHeaders = defaults.headers
const { result, waitForNextUpdate } = renderHook(
() => useFetch(globalOptions => {
// TODO: fix the generics here so it knows when a header
Expand Down
20 changes: 16 additions & 4 deletions src/__tests__/useFetchArgs.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { renderHook } from '@testing-library/react-hooks'
import useFetchArgs, { useFetchArgsDefaults } from '../useFetchArgs'
import useFetchArgs from '../useFetchArgs'
import defaults, { useFetchArgsDefaults } from '../defaults'
import React, { ReactElement, ReactNode } from 'react'
import { Provider } from '..'

Expand Down Expand Up @@ -183,7 +184,11 @@ describe('useFetchArgs: general usages', (): void => {
url: 'https://example.com'
},
requestInit: {
...options
...options,
headers: {
...defaults.headers,
...options.headers
}
}
})
})
Expand All @@ -201,7 +206,10 @@ describe('useFetchArgs: general usages', (): void => {
url: 'http://localhost'
},
requestInit: {
...options
headers: {
...defaults.headers,
...options.headers
}
}
})
})
Expand Down Expand Up @@ -231,7 +239,11 @@ describe('useFetchArgs: general usages', (): void => {
url: 'http://localhost'
},
requestInit: {
...overwriteProviderOptions
...overwriteProviderOptions,
headers: {
...defaults.headers,
...overwriteProviderOptions.headers
}
}
})
})
Expand Down
38 changes: 38 additions & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Flatten, CachePolicies, UseFetchArgsReturn } from './types'
import { isObject } from './utils'


export const useFetchArgsDefaults: UseFetchArgsReturn = {
customOptions: {
cacheLife: 0,
cachePolicy: CachePolicies.CACHE_FIRST,
interceptors: {},
onAbort: () => { /* do nothing */ },
onNewData: (currData: any, newData: any) => newData,
onTimeout: () => { /* do nothing */ },
path: '',
perPage: 0,
persist: false,
retries: 0,
retryDelay: 1000,
retryOn: [],
suspense: false,
timeout: 0,
url: '',
},
requestInit: {
headers: {
Accept: 'application/json, text/plain, */*'
}
},
defaults: {
data: undefined,
loading: false
},
dependencies: undefined
}

export default Object.entries(useFetchArgsDefaults).reduce((acc, [key, value]) => {
if (isObject(value)) return { ...acc, ...value }
return { ...acc, [key]: value }
}, {} as Flatten<UseFetchArgsReturn>)
4 changes: 2 additions & 2 deletions src/doFetchArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export default async function doFetchArgs<TData = any>(
((bodyAs2ndParam as any) instanceof FormData ||
(bodyAs2ndParam as any) instanceof URLSearchParams)
) return bodyAs2ndParam as any
if (isBodyObject(bodyAs2ndParam)) return JSON.stringify(bodyAs2ndParam)
if (isBodyObject(initialOptions.body)) return JSON.stringify(initialOptions.body)
if (isBodyObject(bodyAs2ndParam) || isString(bodyAs2ndParam)) return JSON.stringify(bodyAs2ndParam)
if (isBodyObject(initialOptions.body) || isString(bodyAs2ndParam)) return JSON.stringify(initialOptions.body)
return null
})()

Expand Down
26 changes: 26 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,32 @@ export type OverwriteGlobalOptions = (options: Options) => Options
export type RetryOn = (<TData = any>({ attempt, error, response }: { attempt: number, error: Error, response: Res<TData> | null }) => boolean) | number[]
export type RetryDelay = (<TData = any>({ attempt, error, response }: { attempt: number, error: Error, response: Res<TData> | null }) => number) | number

export type UseFetchArgsReturn = {
customOptions: {
cacheLife: number
cachePolicy: CachePolicies
interceptors: Interceptors
onAbort: () => void
onNewData: (currData: any, newData: any) => any
onTimeout: () => void
path: string
perPage: number
persist: boolean
retries: number
retryDelay: RetryDelay
retryOn: RetryOn | undefined
suspense: boolean
timeout: number
url: string
}
requestInit: RequestInit
defaults: {
loading: boolean
data?: any
}
dependencies?: any[]
}

/**
* Helpers
*/
Expand Down
Loading