-
-
Notifications
You must be signed in to change notification settings - Fork 96
RFC: useNewFetcher() #760
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
Comments
@ntucker at first glance this seems neat but to be honest I think I'm lacking some detail around |
@praxxis Thanks for the response. Updated the description pretty heavily - both based on your questions and some evolution of my thoughts. Also reorganized the post to clearly delineate each change in its own section. |
Adding an instance I encountered where a two stage promise would be helpful. This is within a modal that deletes a resource and closes on delete success.
In this case, the component is still mounted when the delete completes, so |
@sweetro This is very unexpected. It should be resolving that promise before the state is updated - unless you're using optimistic updates? If optimistic updates are used you'll want to just rearrange the lines: const data = useResource(SomeResource.detail() { id })
const doDelete = useFetcher(SomeResource.delete())
// later on
onClick = async () => {
closeModal()
await doDelete({ id })
} |
Resolved with #1239 |
Would love feedback @dair-targ, @smith-it @jamakase @Secretmapper @Jsarihan @yunyu @praxxis @zacharyfmarion @kmartinezmedia
Motivation
Fixes #463, #458, #428, #316, #358
Lots of improvements can be made to useFetcher() design - both based on understanding of use cases, as well as evolution of interfaces like Endpoint.
Providing a new hook allows for incremental adoption of a complete redesign of many of the concepts incorporating 2 years of learnings and experience across many companies.
There are two major areas of change here
References:
class User
Summary:
Details
One hook, many endpoints
The rules of hooks are very restrictive, so the less hooks you have to call, the more flexible. This also benefits render performance. In many cases you might want to fetch many different endpoints. What's worse is if you don't know which endpoints you might want to fetch upfront. With old design you'd have to hook up every possible one. This really destroys fetch-as-render pattern, as you want to be able to prefetch based on possible routes.
Before
After
Completely flexible, variable arguments
The concept of params + body for arguments was introduced to try to provide the most flexible approach in a world where type enforcement wasn't that flexible. With TypeScript 4's variadic tuples, it's now possible to strongly type arbitrary arguments to a function in a generic way. Furthermore, stumbling upon package.json's typeVersions, rest hooks can now publish multiple type versions to be compatible with different versions of typescript. This allows us to eagerly adopt TypeScript 4 features, while providing a usable TypeScript 3 experience.
Some common annoyances with the current parameter limitations are single-variable arguments like detail endpoints with an id, as well as no-argument case like a list endpoint or create endpoint.
We'll also eventually bring this to the 'read' hooks like so:
Resolve/return value
Before
Currently the promise from a useFetcher() resolves with exactly the data from the endpoint's fetch.
This is problematic for a few reasons:
Denormalize<Scheam>
of the endpoint, which often deviates from the value by having classes instead of pojos.Currently the resolution of the promise happens upon commit of the updates to the cache state. This guarantees any updates from the fetch exist in the current react render/DOM before processing the next step.
After
Idea 1) Just change resolution to denormalized value
Pros:
Cons:
Idea 2) Two stage promises
This sort of matches the fetch pattern - where the headers like
status
are available immediately, butjson()
returns another promise.This also enables distinguishing the stages of a fetch - the resolution of fetch itself, and the commit of the updates to the DOM and react tree.
Pros
Cons
.data
.Endpoint definition - new updater
By normalizing Entities, Rest Hooks guarantees data integrity and consistency even down to the referential equality level. However, there are still some cases where side effects result in changes to the actual results themselves. The most common reason for this is creation of new entities. While 'creation' is almost universally the cause for this (as deletion is handled more simply by delete schemas), the structure of data and where created elements go is not universal.
Before
Previously this was enabled by an optional third argument to the fetch UpdateParams enabling programmatic changes that are also strictly type enforced to ensure the data integrity of the Rest Hooks store.
While simple, this design had several shortcomings
.extend()
to contextually override so this feature is moot.After
Simplest case:
More updates:
Component.tsx
The endpoint below ensures the new user shows up immediately in the usages above.
userEndpoint.ts
Extracting patterns
In case more than one other endpoint might result in updating our list endpoint, we can centralize the logic of how that should work in our updated endpoint.
Alternate Ideas - The programmatic approach
The no guarantees
Store Adapter
Another idea is to make an updater callback with identical API to manager middleware. We would probably want to minimize chaining actions, so some way of consolidating into one action would be preferable. Adding an adapter to raw state might be good for Manager's as well, so designing this interface could be beneficial to optionally improving middleware interfaces.
The text was updated successfully, but these errors were encountered: