Skip to content

Commit 1b66fb5

Browse files
committed
allow passing a callback to useFetch's run to modify init
1 parent f22acd0 commit 1b66fb5

File tree

3 files changed

+35
-6
lines changed

3 files changed

+35
-6
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ const MyComponent = () => {
191191
const headers = { Accept: "application/json" }
192192
const { data, error, isLoading, run } = useFetch("/api/example", { headers }, options)
193193
// This will setup a promiseFn with a fetch request and JSON deserialization.
194+
195+
// you can later call `run` with an optional callback argument to
196+
// last-minute modify the `init` parameter that is passed to `fetch`
197+
function clickHandler() {
198+
run(init => ({
199+
...init,
200+
body: JSON.stringify(formValues)
201+
}))
202+
}
194203
}
195204
```
196205

packages/react-async/src/useAsync.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,7 @@ const useAsync = (arg1, arg2) => {
126126

127127
const parseResponse = (accept, json) => res => {
128128
if (!res.ok) return Promise.reject(res)
129-
if (json === false) return res
130-
if (json === true || accept === "application/json") return res.json()
129+
if (json === true || (json !== false && accept === "application/json")) return res.json()
131130
return res
132131
}
133132

@@ -136,13 +135,17 @@ const useAsyncFetch = (input, init, { defer, json, ...options } = {}) => {
136135
const headers = input.headers || (init && init.headers) || {}
137136
const accept = headers["Accept"] || headers["accept"] || (headers.get && headers.get("accept"))
138137
const doFetch = (input, init) => globalScope.fetch(input, init).then(parseResponse(accept, json))
139-
const isDefer = defer === true || ~["POST", "PUT", "PATCH", "DELETE"].indexOf(method)
140-
const fn = defer === false || !isDefer ? "promiseFn" : "deferFn"
138+
const isDefer =
139+
defer === true || (defer !== false && ~["POST", "PUT", "PATCH", "DELETE"].indexOf(method))
140+
const fn = isDefer ? "deferFn" : "promiseFn"
141141
const state = useAsync({
142142
...options,
143143
[fn]: useCallback(
144-
(_, props, ctrl) => doFetch(input, { signal: ctrl ? ctrl.signal : props.signal, ...init }),
145-
[JSON.stringify(input), JSON.stringify(init)]
144+
isDefer
145+
? ([override], _, { signal }) =>
146+
doFetch(input, { signal, ...(typeof override === "function" ? override(init) : init) })
147+
: (_, { signal }) => doFetch(input, { signal, ...init }),
148+
[(isDefer, JSON.stringify(input), JSON.stringify(init))]
146149
),
147150
})
148151
useDebugValue(state, ({ counter, status }) => `[${counter}] ${status}`)

packages/react-async/src/useAsync.spec.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,21 @@ describe("useFetch", () => {
161161
await Promise.resolve()
162162
expect(json).toHaveBeenCalled()
163163
})
164+
165+
test("calling `run` with an argument allows to override `init` parameters", () => {
166+
const component = (
167+
<Fetch input="/test" init={{ method: "POST" }}>
168+
{({ run }) => (
169+
<button onClick={() => run(init => ({ ...init, body: '{"name":"test"}' }))}>run</button>
170+
)}
171+
</Fetch>
172+
)
173+
const { getByText } = render(component)
174+
expect(globalScope.fetch).not.toHaveBeenCalled()
175+
fireEvent.click(getByText("run"))
176+
expect(globalScope.fetch).toHaveBeenCalledWith(
177+
"/test",
178+
expect.objectContaining({ method: "POST", signal: abortCtrl.signal, body: '{"name":"test"}' })
179+
)
180+
})
164181
})

0 commit comments

Comments
 (0)