Skip to content
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

refactor(useFetch): change internal logic #549

Closed
wants to merge 33 commits into from
Closed

refactor(useFetch): change internal logic #549

wants to merge 33 commits into from

Conversation

userquin
Copy link
Contributor

@userquin userquin commented May 30, 2021

closes #542
closes #539
closes #553
closes #526

@userquin userquin changed the title fix(useFetch): wrong payloadType assigment on setMethod fix(useFetch): wrong payloadType assignment on setMethod May 30, 2021
@userquin
Copy link
Contributor Author

Dont merge yet, I Will change this PR to correct content-type and we need also handle form data in the assignment if the payload type is not provided.

feat: added `formEncoded` allowing submission of forms
feat: added `application/x-www-form-urlencoded` handling via URLSearchParams
@userquin userquin changed the title fix(useFetch): wrong payloadType assignment on setMethod fix(useFetch): correct content-type handling May 30, 2021
@userquin
Copy link
Contributor Author

userquin commented May 30, 2021

the headers should be handled before the fetch call, since the user can provide them on context. If multipart/form is requested and the user include the header, the server will fail to extract submitted files.

@userquin
Copy link
Contributor Author

userquin commented May 30, 2021

I need to do one more thing, encode the data when using formEncoded when payload is not URLSEarchParams, so don't merge yet, I'm working on it rigth now.

@userquin
Copy link
Contributor Author

I need to add some tests for application/x-www-form-urlencoded and multipart/form-data payload types.

@userquin
Copy link
Contributor Author

userquin commented May 30, 2021

@wheatjs it is confusing the logic inside execute method: since we can override the body from context options, and before calling fetch we are using context.options after defaultOptions when merging to create the RequestInit object, the logic handling the payload and the headers can be moved before the Promise and we can also use context.options.body if provided instead the config one.

I think we can change the behavior and so we can use the context.options to reuse the execute method. I will wait your feedback before changing anything.

@userquin
Copy link
Contributor Author

@wheatjs I will change it so that you can compare with the original, looking at changes you will see what I mean.

@userquin
Copy link
Contributor Author

@wheatjs I cannot figure out how to include jest tests for multipart/form-data and for application/x-www-form-urlencoded, since we are mocking the request.

@userquin
Copy link
Contributor Author

userquin commented May 30, 2021

@wheatjs the only way I have found is to include it on the demo (I will also add application/x-www-form-urlencoded in the page):

imagen

once executed (can be executed multiple times, with blob content changing between request)

imagen

@userquin
Copy link
Contributor Author

Also application/x-www-form-urlencoded included:

imagen

once executed (can be executed multiple times, with form content changing between request)

imagen

@userquin
Copy link
Contributor Author

@wheat

With this PR useFetch will work as expected, I'll try to fix the change of the content type once execute is called first time.

I don't know why don't allow to change the config object once initialized, I think it can be modified just not allowing to change it while fetch is in progress.

Why is setMethod returning undefined if initialized is true? Is this intentional or something that was included time ago and now it is not necessary?

@wheatjs
Copy link
Member

wheatjs commented Jun 2, 2021

Why is setMethod returning undefined if initialized is true? Is this intentional or something that was included time ago and now it is not necessary?

This is on purpose as we don't want to allow users to change the method after they've already set it. I think this is probably okay to merge though @antfu. There are a few other changes that need to be made to useFetch before v5.0 official release so I'd like to get this finished so I can work on the others without to many merge conflicts.

@userquin userquin changed the title fix(useFetch): correct content-type handling refactor(useFetch): change internal logic Jun 3, 2021
@userquin userquin requested a review from wheatjs June 3, 2021 16:29
@userquin
Copy link
Contributor Author

userquin commented Jun 3, 2021

I think the PR is done, waiting for reviews, tmr I'm taking a vacation until next Monday / Tuesday ... but I'll do it with an eye here.

@userquin
Copy link
Contributor Author

userquin commented Jun 3, 2021

One last thing I must include: some tests for encoding. I cannot figure out how to test formEncoded and formData with jest, since we need to mock request and response and there is no real server behind.
See you next week...

…formEncoded` and `formData`

docs: rephrasing some entries on `formEncoded` and `formData`
@wheatjs
Copy link
Member

wheatjs commented Jun 4, 2021

I feel like this PR is doing to much and it is getting hard to review. Perhaps you can refocus this PR on the original problem is was solving and we can address the other fetch issues on another branch

@userquin
Copy link
Contributor Author

userquin commented Jun 4, 2021

I feel like this PR is doing to much and it is getting hard to review. Perhaps you can refocus this PR on the original problem is was solving and we can address the other fetch issues on another branch

Can you create a branch and merge this pr on it? Maybe this will help to review it.

error: false,
errorMessage: undefined
}
switch(response.status) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this should be:

if (response.ok) {
   context.data = await response.blob();
} else {
   // handle exceptional cases here.
}

Copy link
Contributor

@benlesh benlesh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the drive-by review. I saw this PR in here while I was submitting one of my own, and I've recently done a lot of work in this area (for RxJS).

This PR looks like it was a lot of very well thought out work and consideration, but I would advise against merging it in its current state for a few reasons:

  1. It adds a lot more weight to what should otherwise be a pretty light-weight helper.
  2. The handling of setting content-type is weighty and incorrect.
  3. The area that deals with URLSearchParams will add additional JSON parsing burden to any server consuming what is (in the majority case) supposed to be simple value types. And it's a lot of unnecessary work.
  4. Most of the ergonomics gains that could be made from this PR seem like they may result in unintentional pain and a lot of extra weight to cover what, in my opinion, would be edge cases that developers could easily handle with their own code.

Again, this is just my general review that was completely unsolicited, and I really respect the author for putting in the effort and sticking to the debate in this PR.

I think maybe a much leaner, smaller, incremental step, with obvious value, might be attainable by refactoring this PR.

@@ -168,6 +185,46 @@ function isFetchOptions(obj: object): obj is UseFetchOptions {
return containsProp(obj, 'immediate', 'refetch', 'beforeFetch', 'afterFetch')
}

async function toURLSearchParams(body: any): Promise<URLSearchParams> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the purpose of this function. The goal seems to be to deal with the fact that the user may have accidentally passed a search param that is an object, rather than a primitive value type.

  1. I'm not sure why it's asynchronous. I don't see any async work being done.
  2. It doesn't seem like a good idea to JSON encode everything, because then whatever is on the other end of the network will have to JSON decode each value. In other words, it will get a bunch of key/value pairs, and then be forced to JSON.parse (or the server language equivalent) for every single value in there, even if they're all strings.

I strongly think the onus of doing this work (and deciding if it's even necessary) should be on the developer making the fetch, and we should throw this logic out and let them handle it. They know their server (presumably). And this is covering a use case that generally doesn't exist in the 99% case. Most of the time, if someone is sending search params, they're value-type primitives, not objects. This is penalizing the 99% use case to accommodate a one-off scenario.

if (config.payload) {
let payload = context.options?.body || config.payload

if (payload) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the work below here does not need to be done. fetch and it's related types, like Request, will automatically figure out the proper Content-Type header.

image

It's worth noting that this is even the case with XHR. I recently wrote some code related to this for XHR, which handles this that is much more concise. Hopefully that helps.

I strongly advise that we don't do things like check a payloadType configuration or the like, because it will lead to user confusion and odd behaviors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to avoid to include a breaking change.
It is a pain payloadType option, we must deal with it.
The content type must be addressed since can be wrong, for example, adding it when using multipart, you can see the pictures included in this pr.

From a user perspective, it Will be more useful to provide the payload as a raw object rather than using fetch ones,.

I'm waiting to be included in a separated branch.

@wheatjs
Copy link
Member

wheatjs commented Jul 16, 2021

Going to close this out as we plan on introducing useKy instead for advanced fetch usage.

@wheatjs wheatjs closed this Jul 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants