miniplex
miniplex copied to clipboard
[@miniplex/react] Provide an out of the box mechanism for ref capture components
Instead of doing this:
type Entity = {
...,
transform: THREE.Object3D
}
<ECS.Entity>
<ECS.Component name="transform">
<mesh />
</ECS.Component>
</ECS.Entity>
Let's bake support for captured refs into @miniplex/react
! This could look like the following, where the first child of <Entity>
is captured into the ref
component by default:
type Entity = {
...,
ref: THREE.Object3D
}
<ECS.Entity>
<mesh />
<ECS.Component name="health" value={100} />
</ECS.Entity>
Now the ref of the THREE.Mesh
is captured into the ref
component of the world's entity type, which would need to be typed accordingly (ie. ref: THREE.Object3D
.)
For cases where the component name needs to be something different than ref
, we could make this configurable through createReactAPI
, eg.:
/* Set the default ref capture component to sceneObject */
const ECS = createReactAPI(world, { refComponent: "sceneObject" })
Potential Problems
- We can't force
Entity
to only have a single child, because that would block the user from setting additional components through JSX. This means that the remaining heuristic we can apply here is to look for the first child, which feels a little bit iffy/easy to break/etc.
How about
<ECS.Entity create={<mesh>...<mesh/>}>
<ECS.Component name="health" value={100} />
</>
?
How about
<ECS.Entity create={<mesh>...<mesh/>}> <ECS.Component name="health" value={100} /> </>
?
This would sort of go in line with another change I want to implement for 2.0, where you can set components directly as props on the <Entity>
component. There'd be another issue, though: sometimes you may want to have a component contain actual JSX nodes (!= refs to the elements they eventually render), so I imagine this could make typing this accurately a little iffy. But I will keep it in mind.
I'm not sure if I like default ref capturing of the first child. I'm currently doing this in my code and am guessing that it would capture the ModelPlaceholder ref - instead of the Model - in that case.
return (
<ECS.Entity entity={character}>
<Suspense fallback={<ModelPlaceholder dimensions={character.dimensions}
position={character.transformTarget.position.toArray()}/>}>
<ECS.Component name={"sceneObject"}>
<Model />
</ECS.Component>
</Suspense>
</ECS.Entity>
)
Closing this for now until a better pattern emerges. Thanks for the feedback!