ecomfe / react-suspense-boundary

A boundary component working with suspense and error

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

  1. Don't think useResourceRef is necessary, simple useResource will throw is resource is pending.
  2. 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
  3. 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?
  1. useResourceRef is used to get data from resourceRef which is returned by usePreloadResource. Otherwise useResource supports both task and resourceRef, useResourceRef is necessary. Or resourceRef 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 of useResource. Refs rather than task+params are more convenient among components to share preloaded resources.
  2. It's ok. This is actually what useQueryLoader, which returns loadQuery to trigger preload and queryRef to trace result, of Relay does.
  3. 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 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)

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.