Skip to content

Commit

Permalink
allow passing a callback to useFetch's run to modify init
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas committed Aug 9, 2019
1 parent f22acd0 commit 1b66fb5
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 6 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@ const MyComponent = () => {
const headers = { Accept: "application/json" }
const { data, error, isLoading, run } = useFetch("/api/example", { headers }, options)
// This will setup a promiseFn with a fetch request and JSON deserialization.

// you can later call `run` with an optional callback argument to
// last-minute modify the `init` parameter that is passed to `fetch`
function clickHandler() {
run(init => ({
...init,
body: JSON.stringify(formValues)
}))
}
}
```

Expand Down
15 changes: 9 additions & 6 deletions packages/react-async/src/useAsync.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ const useAsync = (arg1, arg2) => {

const parseResponse = (accept, json) => res => {
if (!res.ok) return Promise.reject(res)
if (json === false) return res
if (json === true || accept === "application/json") return res.json()
if (json === true || (json !== false && accept === "application/json")) return res.json()
return res
}

Expand All @@ -136,13 +135,17 @@ const useAsyncFetch = (input, init, { defer, json, ...options } = {}) => {
const headers = input.headers || (init && init.headers) || {}
const accept = headers["Accept"] || headers["accept"] || (headers.get && headers.get("accept"))
const doFetch = (input, init) => globalScope.fetch(input, init).then(parseResponse(accept, json))
const isDefer = defer === true || ~["POST", "PUT", "PATCH", "DELETE"].indexOf(method)
const fn = defer === false || !isDefer ? "promiseFn" : "deferFn"
const isDefer =
defer === true || (defer !== false && ~["POST", "PUT", "PATCH", "DELETE"].indexOf(method))
const fn = isDefer ? "deferFn" : "promiseFn"
const state = useAsync({
...options,
[fn]: useCallback(
(_, props, ctrl) => doFetch(input, { signal: ctrl ? ctrl.signal : props.signal, ...init }),
[JSON.stringify(input), JSON.stringify(init)]
isDefer
? ([override], _, { signal }) =>
doFetch(input, { signal, ...(typeof override === "function" ? override(init) : init) })
: (_, { signal }) => doFetch(input, { signal, ...init }),
[(isDefer, JSON.stringify(input), JSON.stringify(init))]
),
})
useDebugValue(state, ({ counter, status }) => `[${counter}] ${status}`)
Expand Down
17 changes: 17 additions & 0 deletions packages/react-async/src/useAsync.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,21 @@ describe("useFetch", () => {
await Promise.resolve()
expect(json).toHaveBeenCalled()
})

test("calling `run` with an argument allows to override `init` parameters", () => {
const component = (
<Fetch input="/test" init={{ method: "POST" }}>
{({ run }) => (
<button onClick={() => run(init => ({ ...init, body: '{"name":"test"}' }))}>run</button>
)}
</Fetch>
)
const { getByText } = render(component)
expect(globalScope.fetch).not.toHaveBeenCalled()
fireEvent.click(getByText("run"))
expect(globalScope.fetch).toHaveBeenCalledWith(
"/test",
expect.objectContaining({ method: "POST", signal: abortCtrl.signal, body: '{"name":"test"}' })
)
})
})

0 comments on commit 1b66fb5

Please sign in to comment.