Skip to content
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

Combining useQuery with useTrait in one React Component #22

Open
Ctrlmonster opened this issue Dec 9, 2024 · 0 comments
Open

Combining useQuery with useTrait in one React Component #22

Ctrlmonster opened this issue Dec 9, 2024 · 0 comments

Comments

@Ctrlmonster
Copy link
Collaborator

Ctrlmonster commented Dec 9, 2024

Wanted to re-open the discussion from #20
Don't need to decide on this immediately, just want to have it open for visiblity and discussion. It's gotten in the way multiple times past two weeks for me.

Context:

It's not possible to do this:

function FooDisplay() { 
  const entity = useQueryFirst(Foo); // <- entity doesn't exist on first mount
  const fooSnapshot = useTrait(entity, Foo); // <- errors because entity can't be undefined

  return <div>{fooSnapshot?.value}</div>
}

Instead you're currently supposed to the following to make sure the entity exists first:

function FooGetter() { 
  const entity = useQueryFirst(Foo); // <- entity doesn't exist on first mount
  return entity && <FooDisplay /> // <- only mount display once entity exists
}

function FooDisplay({entity}) {
  const fooSnapshot = useTrait(entity, Foo); 
  return <div>{fooSnapshot.value}</div>
}

Why it's not ideal

Mounting components conditionally is not the ideal way to "toggle" in a lot of scenarios. Either because expensive effects are executed on mount, or simply because we'd rather control visibility directly, instead of triggering React tree-updates, i.e. in r3f setting <mesh visible={showElement} instead of un-mounting/mounting the mesh.

Keeping previous functionality

I understand the reasoning of being able to differentiate between did the entity not exist or does the entity exist, but doesn't have the trait. I don't think it's a usecase that comes up enough to justify the overhead. In most cases you just want to display the data once it's there. If you do care about that distinction, you can still do:

const trait = useTrait(entity, Foo);
const entityExistButHasNoTrait = useMemo(() => entity && !trait, [entity, trait]);

I think it's more important that the tools we provide work well together out of the box and can be combined. Getting an entity from the world (i.e. not passed as prop) and reacting to one of its traits is a common thing and should not require you to split up into two different components every time. Additionally there is the potential runtime cost associated with triggering a mount. Imo practicality should go over "sematic correctness" in this case.

As mentioned above, open to discussion, don't need to decide immediately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant