Skip to content

Allow passing arguments to run with useFetch #75

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

Closed
phryneas opened this issue Aug 8, 2019 · 7 comments
Closed

Allow passing arguments to run with useFetch #75

phryneas opened this issue Aug 8, 2019 · 7 comments
Labels
enhancement Enhancement to existing feature good first issue Good for newcomers

Comments

@phryneas
Copy link
Member

phryneas commented Aug 8, 2019

Hi there,
I have the following use case:
I want to use useAsyncFetch in a function component with formik.

That looks something like this:

function MyForm() {
  const { run } = useFetch(
    "https://example.com",
    { method: "POST" },
    { defer: true }
  );

  return (
    <div>
      <h1>My Form</h1>
      <Formik
        initialValues={{ name: "jared" }}
        onSubmit={(values, actions) => {
          console.log('submitting', values)
          // I want to submit here
          run({ body: JSON.stringify(values) } /* this is currently not possible */).then(() =>
            actions.setSubmitting(false)
          );
        }}
        render={props => (
          <form onSubmit={props.handleSubmit}>
            <Field type="text" name="name" placeholder="Name" />
            <button type="submit">Submit</button>
          </form>
        )}
      />
    </div>
  );
}

But currently, that doesn't work, because I have no way of passing arguments into run that actually reflect in the fetch call - and since my values are only available within the onSubmit callback of formik, I have no way of accessing those in the outer scope where useFetch is called.
(Of course, this can be worked around by keeping init in a ref and modifying it before calling run, but that's really smelly)

So my suggestion would be to use the first argument of this function that is currently unused to allow passing an argument that will be spread over init, before the request is sent.

Would something like this be feasible? I'd be happy to do a PR if this is something you'd accept.

@ghengeveld
Copy link
Member

Hi Lenz, thanks for the feedback. That's an excellent suggestion. I would love to accept a PR for this. What you're looking for is right here. Be aware that [fn] here can be either promiseFn or deferFn which have a slightly different signature. The _ is the args passed into run (as an array), so that's the one you want to spread onto the fetch init param. However with promiseFn this will be the props object instead. Something to take into account for sure.

@ghengeveld
Copy link
Member

I think we should have something like this:

(args, props, ctrl) => doFetch(input, ctrl ? { signal: ctrl.signal, ...init, ...args[0] } : { signal: props.signal, ...init })

Note that this spreads only the first argument of run onto the fetch options. We could do the whole array but I see not use case for that. Obviously there's a requirement that this argument must be an object.

@ghengeveld ghengeveld added enhancement Enhancement to existing feature good first issue Good for newcomers labels Aug 13, 2019
@ghengeveld ghengeveld changed the title allow to pass request parameters to the deferred run of useAsyncFetch Allow passing arguments to run with useFetch Aug 13, 2019
@ghengeveld
Copy link
Member

This is merged and will be released in v8.0.0 soon.

@ghengeveld
Copy link
Member

Released in v8.0.0

@swolidity
Copy link

Is there a way to pass dynamic query params to a GET request?

@ghengeveld
Copy link
Member

@iamandyk Good question! I thought this was possible, but upon further investigation, it turns out you can't, at least not using useFetch with run and passing an init object. The first argument currently has to be a string url, and it's impossible to override this using a url prop in the init object.

For the time being you can fall back to using useAsync instead of useFetch, which allows you to do this:

import { useAsync } from "react-async"

const loadCustomer = async ([id], props, { signal }) => {
  const res = await fetch(`/api/customers?id=${id}`, { signal })
  if (!res.ok) throw new Error(res)
  return res.json()
}

const MyComponent = () => {
  const { data, run } = useAsync({ deferFn: loadCustomer })

  return (
    <div>
      {[1, 2, 3].map(id => <Button onClick={() => run(id)}>load #{id}</Button>}
    </div>
  )
}

We'll have to update useFetch to allow overriding the url through init.

@ghengeveld
Copy link
Member

I opened #111 to resolve this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to existing feature good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

3 participants