-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Allow passing loader instances to useLoader
#3151
Comments
Does #3131 satisfy this? |
@CodyJasonBennett That looks almost exactly like what I want! The only difference I see is the caching behavior. For example, in this case: useLoader.preload(gltfLoader, [URL_A, URL_B])
function ComponentA() {
const gltf = useLoader(gltfLoader, URL_A)
} In the current code, the This sandbox shows one way to address this, memoizing load results for each URL per-loader instead of just memoizing loader instances. Something like like: // memoizedResults[<loader>][<url>] = <result> | undefined
const memoizedResults = new WeakMap<Loader<unknown>, Map<string, unknown>>() That way even though |
I'll be implementing your suggestion in our v9 branch. Need to wrangle with types for v8, but should be possible to backport. |
Unfortunately, I haven't been able to backport this since TypeScript struggles to infer types correctly without upgrading three types (breaking change). This is in our v9 release candidate, which we're soon looking to release to stable alongside Drei. |
Problem
Consider this sandbox: https://codesandbox.io/p/sandbox/use-loader-extend-loader-issue-4w5dtq?file=%2Fsrc%2FApp.tsx%3A11%2C32
You might expect that
<ModelA>
loads normally, while<ModelB>
loads with a plugin that turns materials red. However, both models actually appear red. Imagine that<ModelA>
and<ModelB>
live in separate files, or even separate libraries—it becomes quite difficult to debug why this occurs.Or consider this code:
Do you know what
<MyComponent>
code will display without seeing the.preload()
call? The component logic suggests that it loads a resource fromPATH_B
, but the.preload()
call causes it to load the cached resource fromPATH_A
. The fact that the API for these two invocations even allow differentextensions
is confusing.IMO the global side effects of
useLoader
extensions are an anti-pattern that breaks from the immutable, unidirectional flow that I expect in React. The userland code doesn't communicates that one component is mutating some global object that affects other components, which makes it hard to understand runtime behavior from reading the code.Readability aside, this API doesn't allow you to use loaders like you could in vanilla Three, where you might have different loader configurations for different assets of the same type.
Potential Solution
I think it'd make more sense to pass loader instances to
useLoader
rather than a constructor. This makes the shared nature of loaders more obvious in user code, and allows you to use different loader configurations for different resources.It also makes the
.preload
API easier to understand—the current API allows you to pass different configurations across.preload
anduseLoader
invocations even though these are intended to share a loader instance.This sandbox illustrates a possible implementation: https://codesandbox.io/p/sandbox/use-loader-extend-loader-issue-forked-c7kw3z?file=%2Fsrc%2FApp.tsx%3A8%2C1-16%2C5
Notice how here, even if I accidentally pass a different loader instance to
.preload
and theuseLoader
hook, it simply causes a cache miss instead of loading from the wrong path in my component.Maybe there's some way to make this compatible with the existing API too.
The text was updated successfully, but these errors were encountered: