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

feat(react): add react package #294

Merged
merged 20 commits into from
Jan 12, 2023
Merged

feat(react): add react package #294

merged 20 commits into from
Jan 12, 2023

Conversation

frolic
Copy link
Member

@frolic frolic commented Dec 12, 2022

Adds official React MUD package! This will replace the React usages sprinkled into other packages (I'll do a clean up in a breaking follow up).

@@ -0,0 +1,72 @@
{
"name": "@latticexyz/mud-react",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasn't sure what to call this! @latticexyz/react felt incorrect and I think what I actually want is something like @mud/react

@frolic frolic changed the title feat: add mud-react feat(react): add react package Jan 10, 2023
@frolic frolic marked this pull request as ready for review January 10, 2023 23:49
@frolic frolic requested a review from alvrs as a code owner January 10, 2023 23:49
Copy link
Member

@alvrs alvrs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

Let's add a short README.md - this show up on npm's package overview page when releasing this package (like here: https://www.npmjs.com/package/@latticexyz/solecs)

packages/std-client/src/hooks.ts Outdated Show resolved Hide resolved
import { IComputedValue } from "mobx";
import { useEffect, useState } from "react";

// TODO: migrate the rest of the mobx stuff to rxjs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened an issue to track this, let's add a link to the code comment: #339

component: Component<S, Metadata, undefined>,
defaultValue?: ComponentValue<S>
) {
const entities = useEntityQuery([Has(component)]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this hook cause a react rerender every time any value in the component changes or only if the value of the entity specified in the entityIndex param changes?

Copy link
Member Author

@frolic frolic Jan 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh this is a great point. It'd currently reevaluate for anything matching the query (Has(component)). I may rewrite this to not depend on useEntityQuery and be more mindful of rendering only when the single entity value changes.

@frolic frolic requested a review from alvrs January 12, 2023 00:52
Returns the value of the component for the entity, and triggers a re-render as the component is added/removed/updated.

```
const position = useComponentValue(entity, Position);
Copy link
Member Author

@frolic frolic Jan 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While building Emojimon, I was thinking about ECS in terms of entity-first, where "entity" is often the thing to key off of, and components are bits of data on an entity.

However, I noticed our internal APIs (setComponent, removeComponent), etc. all have components first, then entities. I wonder if this is because we internally model a component as a mapping of entities <> values.

How do you feel about the position of the arguments here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's mostly the reason - when thinking of components as tables and entities as rows in that table, it felt more natural to first specify which table to modify and then which row in that table. In general I don't feel strongly about the order of arguments, can totally see how putting entity first is more intuitive when thinking about entities as objects and components as parameters on that object. My main argument for changing the order here to also put component first would be consistency with existing functions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatted with @alvrs some more and here's what I got out of the convo:

Data modeling with MUD can be thought of in one of two ways

  • Entity-first, where the entity is the focus and data (components/values) are "attached" to an entity. I was thinking about it like this, where my "get component value" operation is more like a "get this value of this key from the entity object"
  • Component/Table-first, where it's more like a relational database and the entity ID is a primary key. In the future, we may want more complicated keys (tuples) that would expand past just an entity ID. So asking for a component value is more like "get the value in the component table for this primary key aka entity ID".

Sounds like we want to guide folks towards the latter mental model, esp. as we push further into SQL-like capability.

Will update these hooks!

@frolic
Copy link
Member Author

frolic commented Jan 12, 2023

Quickly wired this into Emojimon to make sure it works and to see what the diff looks like:
latticexyz/emojimon@74629a8

Copy link
Member

@alvrs alvrs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Just a some minor comments and a minor preference for changing the order of parameters for consistency.

Returns the value of the component for the entity, and triggers a re-render as the component is added/removed/updated.

```
const position = useComponentValue(entity, Position);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's mostly the reason - when thinking of components as tables and entities as rows in that table, it felt more natural to first specify which table to modify and then which row in that table. In general I don't feel strongly about the order of arguments, can totally see how putting entity first is more intuitive when thinking about entities as objects and components as parameters on that object. My main argument for changing the order here to also put component first would be consistency with existing functions.

"jest": "^29.3.1",
"mobx": "^6.4.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is react-dom also required as peer dependency?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope! will remove

expect(result.current).toBe(undefined);
});

it("should re-render only when Position changes for entity", () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love these tests!! 🔥

packages/react/src/useComponentValue.ts Outdated Show resolved Hide resolved
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"exclude": ["dist", "**/*.test.ts", "packages"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooc why do we need to exclude packages here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not needed, this was just copy pasta

@alvrs
Copy link
Member

alvrs commented Jan 12, 2023

Should we add you as the codeowner for this package to auto-request reviews from? https://github.com/latticexyz/mud/blob/main/CODEOWNERS (Can also set the codeowner for ecs-browser to kooshaba while at it)

Co-authored-by: alvarius <89248902+alvrs@users.noreply.github.com>
@frolic frolic requested a review from alvrs January 12, 2023 19:48
Copy link
Member

@alvrs alvrs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing! 🚢

@alvrs alvrs merged commit f5ee290 into main Jan 12, 2023
LPSCRYPT pushed a commit to LPSCRYPT/esp that referenced this pull request Jan 23, 2023
* feat: add mud-react

* feat: rename mud-react to react

* chore(react): clean up

* chore(react): rename mobx-dependent module

* feat(react): add useObservableValue hook

* chore(react): add deprecation notice

* chore(react): remove unused deps

* fix(react): version

* fix(react): use @deprecated tag

Co-authored-by: alvarius <89248902+alvrs@users.noreply.github.com>

* chore(react): more deprecated notices

* test(react): set up test framework, add tests for useEntityQuery

* test(react): add tests for useComponentValue

* test(react): refactor useComponentValue to only re-render when entity's value changes

* docs(react): add README

* fix(react): use typed helper for checking if it's a component update

Co-authored-by: alvarius <89248902+alvrs@users.noreply.github.com>

* chore(react): add frolic to codeowners

* refactor(react): change useComponentValue args ordering

* style(react): simplify tsconfig

* fix(react): remove unused dep

Co-authored-by: alvarius <89248902+alvrs@users.noreply.github.com>
@alvrs alvrs deleted the holic/mud-react branch February 7, 2023 15:34
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

Successfully merging this pull request may close these issues.

2 participants