Introduce `usePreloadResource` to reduce serial loads and improve hooks
599316527 opened this issue · comments
useResource
is required to be executed inside Boundary
. So async tasks like http requests have to be called inside components which are descendants of Boundary. If in a nested lazy loading component, the request is sent much later than if it was sent at the beginning.
And it limits the abstraction of hooks as well. Combined states and actions are separated from hooks into descendants components and parameters are also passed to them to initialize tasks wastefully.
So inspired by facebook/relay, the problem could be sovled by splitting useResource
into two parts. The one part (usePreloadResource
) just initialize the task and the other (useResourceRef
) reads the result and trigger suspense if the result is not ready.
Expected usage:
function App() {
const [resource] = usePreloadResource(task, params);
return <>
....
<Boundary>
<Panel resource={resource} />
</Boundary>
....
</>;
}
function Panel({resource}) {
const {items} = useResourceRef(resource); // will throw suspense before resource is ready
return ...
}
Here's the demo: https://codesandbox.io/s/wild-worker-il981?file=/src/hook.js
- Don't think
useResourceRef
is necessary, simpleuseResource
will throw is resource is pending. usePreloadResource
by itself should returns a callback function which triggers preload on invocation, so it is possible to preload, for example, on a user click- Provide a
useImmediatePreloadResource
to preload resource on the fly, but we may discuss the that preload should trigger at either render phase or the first mount effect?
useResourceRef
is used to get data from resourceRef which is returned byusePreloadResource
. OtherwiseuseResource
supports both task and resourceRef,useResourceRef
is necessary. OrresourceRef
itself as a function to produce data or throw pending when being called. And since preload task is triggered in upper component, task and params are not intent on being passed to lower components as arguments ofuseResource
. Refs rather than task+params are more convenient among components to share preloaded resources.- It's ok. This is actually what
useQueryLoader
, which returnsloadQuery
to trigger preload andqueryRef
to trace result, of Relay does. - I think it could works on the same way as
useResource
. Once task or params are changed, reload is called.
// In parent
useImmediatePreloadResource(api.list, params);
// In child
useResource(api.list, params);
api
+ params
always behaves as the key to resource, I don't think it is necessary to introduce a resource
object and pass it to child, since api
is mostly static, passing params
has the same or less complexity compare to a standalone resource
object
I noticed that your idea is to decouple a business aware params
from a child component, this maybe a chioce, in this way usePreloadResource
returns a resourceKey
object (and maybe it is simply a combination of api
and params
), and useResourceRef
can be a overload of useResource(resourceKey)
I noticed that your idea is to decouple a business aware
params
from a child component, this maybe a chioce, in this wayusePreloadResource
returns aresourceKey
object (and maybe it is simply a combination ofapi
andparams
), anduseResourceRef
can be a overload ofuseResource(resourceKey)
But this is not suitable when usePreloadResource
returning a trigger function, storing a dynamically invoked function's return value is much too complicated
To conclusion, I believe "preload" should be transparent to "actual load", preload works like a cache, in this case I still don't want to introduce a resource ref object
OK. Maybe I can wrap the feature upon useResource
by storing refs to task+params by myself.