diff --git a/.changeset/good-eagles-speak.md b/.changeset/good-eagles-speak.md
new file mode 100644
index 00000000..f8b2292e
--- /dev/null
+++ b/.changeset/good-eagles-speak.md
@@ -0,0 +1,16 @@
+---
+"@dojoengine/sdk": minor
+"template-vite-ts": minor
+"@dojoengine/core": minor
+"@dojoengine/create-burner": minor
+"@dojoengine/create-dojo": minor
+"@dojoengine/predeployed-connector": minor
+"@dojoengine/react": minor
+"@dojoengine/state": minor
+"@dojoengine/torii-client": minor
+"@dojoengine/torii-wasm": minor
+"@dojoengine/utils": minor
+"@dojoengine/utils-wasm": minor
+---
+
+feat: add reusable sdk react hooks
diff --git a/examples/example-nodejs-bot/tsconfig.json b/examples/example-nodejs-bot/tsconfig.json
index fdca51ed..54045688 100644
--- a/examples/example-nodejs-bot/tsconfig.json
+++ b/examples/example-nodejs-bot/tsconfig.json
@@ -14,8 +14,7 @@
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
- "outDir": "dist",
- "types": ["bun-types"]
+ "outDir": "dist"
},
"include": [
"./src/**/*",
diff --git a/examples/example-vite-react-sdk/dojoConfig.ts b/examples/example-vite-react-sdk/dojoConfig.ts
index e45ee85a..484d368d 100644
--- a/examples/example-vite-react-sdk/dojoConfig.ts
+++ b/examples/example-vite-react-sdk/dojoConfig.ts
@@ -1,6 +1,6 @@
import { createDojoConfig } from "@dojoengine/core";
-import manifest from "../../worlds/dojo-starter/manifest_dev.json";
+import manifest from "../../../dojo-starter/manifest_dev.json";
export const dojoConfig = createDojoConfig({
manifest,
diff --git a/examples/example-vite-react-sdk/package.json b/examples/example-vite-react-sdk/package.json
index 999df01d..13fc89c5 100644
--- a/examples/example-vite-react-sdk/package.json
+++ b/examples/example-vite-react-sdk/package.json
@@ -21,8 +21,8 @@
"@starknet-react/core": "catalog:",
"@types/uuid": "^10.0.0",
"immer": "^10.1.1",
- "react": "^18.3.1",
- "react-dom": "^18.3.1",
+ "react": "catalog:",
+ "react-dom": "catalog:",
"starknet": "catalog:",
"uuid": "^10.0.0",
"vite-plugin-top-level-await": "^1.5.0",
diff --git a/examples/example-vite-react-sdk/src/App.tsx b/examples/example-vite-react-sdk/src/App.tsx
index 386fd4cf..072ea224 100644
--- a/examples/example-vite-react-sdk/src/App.tsx
+++ b/examples/example-vite-react-sdk/src/App.tsx
@@ -1,14 +1,18 @@
-import { useEffect, useMemo } from "react";
import { KeysClause, ToriiQueryBuilder } from "@dojoengine/sdk";
-import { getEntityIdFromKeys } from "@dojoengine/utils";
-import { AccountInterface, addAddressPadding, CairoCustomEnum } from "starknet";
import { ModelsMapping } from "./typescript/models.gen.ts";
import { useSystemCalls } from "./useSystemCalls.ts";
import { useAccount } from "@starknet-react/core";
import { WalletAccount } from "./wallet-account.tsx";
import { HistoricalEvents } from "./historical-events.tsx";
-import { useDojoSDK, useModel } from "@dojoengine/sdk/react";
+import {
+ useDojoSDK,
+ useEntityId,
+ useEntityQuery,
+ useModel,
+} from "@dojoengine/sdk/react";
+import { addAddressPadding, CairoCustomEnum } from "starknet";
+import { Events } from "./events.tsx";
/**
* Main application component that provides game functionality and UI.
@@ -17,79 +21,26 @@ import { useDojoSDK, useModel } from "@dojoengine/sdk/react";
* @param props.sdk - The Dojo SDK instance configured with the game schema
*/
function App() {
- const { useDojoStore, client, sdk } = useDojoSDK();
+ const { useDojoStore, client } = useDojoSDK();
const { account } = useAccount();
- const state = useDojoStore((state) => state);
const entities = useDojoStore((state) => state.entities);
const { spawn } = useSystemCalls();
- const entityId = useMemo(() => {
- if (account) {
- return getEntityIdFromKeys([BigInt(account.address)]);
- }
- return BigInt(0);
- }, [account]);
-
- // This is experimental feature.
- // Use those queries if you want to be closer to how you should query your ecs system with torii
- // useEffect(() => {
- // async function fetchToriiClause() {
- // const res = await sdk.client.getEntities(
- // new ToriiQueryBuilder()
- // .withClause(
- // new ClauseBuilder()
- // .keys([], [undefined], "VariableLen")
- // .build()
- // )
- // .withLimit(2)
- // .addOrderBy(ModelsMapping.Moves, "remaining", "Desc")
- // .build()
- // );
- // return res;
- // }
- // fetchToriiClause().then(console.log);
- // });
-
- useEffect(() => {
- let unsubscribe: (() => void) | undefined;
-
- const subscribe = async (account: AccountInterface) => {
- const [initialData, subscription] = await sdk.subscribeEntityQuery({
- query: new ToriiQueryBuilder()
- .withClause(
- // Querying Moves and Position models that has at least [account.address] as key
- KeysClause(
- [ModelsMapping.Moves, ModelsMapping.Position],
- [addAddressPadding(account.address)],
- "VariableLen"
- ).build()
- )
- .includeHashedKeys(),
- callback: ({ error, data }) => {
- if (error) {
- console.error("Error setting up entity sync:", error);
- } else if (data && data[0].entityId !== "0x0") {
- state.updateEntity(data[0]);
- }
- },
- });
-
- state.setEntities(initialData);
-
- unsubscribe = () => subscription.cancel();
- };
-
- if (account) {
- subscribe(account);
- }
-
- return () => {
- if (unsubscribe) {
- unsubscribe();
- }
- };
- }, [sdk, account, state]);
+ const entityId = useEntityId(account?.address ?? "0");
+
+ useEntityQuery(
+ new ToriiQueryBuilder()
+ .withClause(
+ // Querying Moves and Position models that has at least [account.address] as key
+ KeysClause(
+ [ModelsMapping.Moves, ModelsMapping.Position],
+ [addAddressPadding(account?.address ?? "0")],
+ "FixedLen"
+ ).build()
+ )
+ .includeHashedKeys()
+ );
const moves = useModel(entityId as string, ModelsMapping.Moves);
const position = useModel(entityId as string, ModelsMapping.Position);
@@ -253,6 +204,7 @@ function App() {
+
{/* // Here sdk is passed as props but this can be done via contexts */}
diff --git a/examples/example-vite-react-sdk/src/events.tsx b/examples/example-vite-react-sdk/src/events.tsx
new file mode 100644
index 00000000..452d7ab4
--- /dev/null
+++ b/examples/example-vite-react-sdk/src/events.tsx
@@ -0,0 +1,40 @@
+import { KeysClause, ToriiQueryBuilder } from "@dojoengine/sdk";
+import { useEntityId, useEventQuery, useModel } from "@dojoengine/sdk/react";
+import { useAccount } from "@starknet-react/core";
+import { addAddressPadding } from "starknet";
+import { ModelsMapping } from "./typescript/models.gen";
+
+export function Events() {
+ const { account } = useAccount();
+ const entityId = useEntityId(account?.address ?? "0");
+ useEventQuery(
+ new ToriiQueryBuilder()
+ .withClause(
+ KeysClause(
+ [],
+ [addAddressPadding(account?.address ?? "0")],
+ "VariableLen"
+ ).build()
+ )
+ .includeHashedKeys()
+ );
+ const moved = useModel(entityId, ModelsMapping.Moved);
+ if (!account) {
+ return (
+
+
Please connect your wallet
+
+ );
+ }
+ return (
+
+
+ Player Last Movement : {moved && moved.direction}{" "}
+
+
+ {/* {events.map((e: ParsedEntity, key) => {
+ return ;
+ })} */}
+
+ );
+}
diff --git a/examples/example-vite-react-sdk/src/historical-events.tsx b/examples/example-vite-react-sdk/src/historical-events.tsx
index a8805940..ae38b878 100644
--- a/examples/example-vite-react-sdk/src/historical-events.tsx
+++ b/examples/example-vite-react-sdk/src/historical-events.tsx
@@ -1,54 +1,20 @@
import { KeysClause, ParsedEntity, ToriiQueryBuilder } from "@dojoengine/sdk";
import { useAccount } from "@starknet-react/core";
import { SchemaType } from "./typescript/models.gen";
-import { AccountInterface, addAddressPadding } from "starknet";
-import { useEffect, useState } from "react";
-import { Subscription } from "@dojoengine/torii-client";
-import { useDojoSDK } from "@dojoengine/sdk/react";
+import { addAddressPadding } from "starknet";
+import { useHistoricalEventsQuery } from "@dojoengine/sdk/react";
export function HistoricalEvents() {
const { account } = useAccount();
- const { sdk } = useDojoSDK();
- const [events, setEvents] = useState[]>([]);
- const [subscription, setSubscription] = useState(null);
-
- useEffect(() => {
- async function subscribeHistoricalEvent(account: AccountInterface) {
- try {
- const [e, s] = await sdk.subscribeEventQuery({
- query: new ToriiQueryBuilder().withClause(
- KeysClause(
- [],
- [addAddressPadding(account.address)],
- "VariableLen"
- ).build()
- ),
- callback: ({ data, error }) => {
- if (data && data.length > 0) {
- console.log(data);
- }
- if (error) {
- console.error(error);
- }
- },
- historical: true,
- });
- setEvents(e as unknown as ParsedEntity[]);
- setSubscription(s);
- } catch (error) {
- setEvents([]);
- if (subscription) {
- subscription.free();
- }
- console.error(error);
- }
- }
-
- if (account) {
- subscribeHistoricalEvent(account);
- }
- }, [account, setEvents, sdk]);
-
+ const events = useHistoricalEventsQuery(
+ new ToriiQueryBuilder().withClause(
+ KeysClause(
+ [],
+ [addAddressPadding(account?.address ?? "0")],
+ "VariableLen"
+ ).build()
+ )
+ );
if (!account) {
return (
@@ -59,6 +25,7 @@ export function HistoricalEvents() {
return (
Player Events :
+ {/* @ts-ignore */}
{events.map((e: ParsedEntity
, key) => {
return ;
})}
diff --git a/examples/example-vite-react-sdk/src/starknet-provider.tsx b/examples/example-vite-react-sdk/src/starknet-provider.tsx
index 43554781..ea314545 100644
--- a/examples/example-vite-react-sdk/src/starknet-provider.tsx
+++ b/examples/example-vite-react-sdk/src/starknet-provider.tsx
@@ -32,6 +32,7 @@ export default function StarknetProvider({ children }: PropsWithChildren) {
explorer={voyager}
autoConnect
>
+ {/* @ts-ignore react version mismatch */}
{children}
);
diff --git a/examples/example-vite-react-sdk/tsconfig.app.tsbuildinfo b/examples/example-vite-react-sdk/tsconfig.app.tsbuildinfo
index ce91cf1a..f71190e8 100644
--- a/examples/example-vite-react-sdk/tsconfig.app.tsbuildinfo
+++ b/examples/example-vite-react-sdk/tsconfig.app.tsbuildinfo
@@ -1 +1 @@
-{"root":["./src/app.tsx","./src/historical-events.tsx","./src/main.tsx","./src/starknet-provider.tsx","./src/usesystemcalls.ts","./src/vite-env.d.ts","./src/wallet-account.tsx","./src/typescript/contracts.gen.ts","./src/typescript/models.gen.ts"],"version":"5.7.3"}
\ No newline at end of file
+{"root":["./src/app.tsx","./src/events.tsx","./src/historical-events.tsx","./src/main.tsx","./src/starknet-provider.tsx","./src/usesystemcalls.ts","./src/vite-env.d.ts","./src/wallet-account.tsx","./src/typescript/contracts.gen.ts","./src/typescript/models.gen.ts"],"version":"5.7.3"}
\ No newline at end of file
diff --git a/examples/example-vite-react-sql/src/hooks/usePlayerActions.ts b/examples/example-vite-react-sql/src/hooks/usePlayerActions.ts
index 184646c5..4686d8fc 100644
--- a/examples/example-vite-react-sql/src/hooks/usePlayerActions.ts
+++ b/examples/example-vite-react-sql/src/hooks/usePlayerActions.ts
@@ -1,62 +1,20 @@
-import { useEffect, useMemo } from "react";
-
-import { KeysClause, ParsedEntity, ToriiQueryBuilder } from "@dojoengine/sdk";
-import { useDojoSDK } from "@dojoengine/sdk/react";
-import { getEntityIdFromKeys } from "@dojoengine/utils";
-import { ModelsMapping, SchemaType } from "@/typescript/models.gen";
+import { KeysClause, ToriiQueryBuilder } from "@dojoengine/sdk";
+import { useEntityId, useEntityQuery } from "@dojoengine/sdk/react";
+import { ModelsMapping, type SchemaType } from "@/typescript/models.gen";
import { addAddressPadding } from "starknet";
export function usePlayerActions(address: string | undefined) {
- const { sdk, useDojoStore } = useDojoSDK();
- const state = useDojoStore((state) => state);
-
- const entityId = useMemo(() => {
- if (address) {
- return getEntityIdFromKeys([BigInt(address)]);
- }
- return BigInt(0);
- }, [address]);
-
- useEffect(() => {
- let unsubscribe: (() => void) | undefined;
-
- const subscribe = async (address: string) => {
- const [entities, subscription] = await sdk.subscribeEntityQuery({
- query: new ToriiQueryBuilder()
- .withClause(
- KeysClause(
- [ModelsMapping.Moves, ModelsMapping.Position],
- [addAddressPadding(address)],
- "VariableLen"
- ).build()
- )
- .includeHashedKeys(),
- callback: ({ error, data }) => {
- if (error) {
- console.error("Error setting up entity sync:", error);
- } else if (
- data &&
- (data[0] as ParsedEntity).entityId !== "0x0"
- ) {
- state.updateEntity(data[0] as ParsedEntity);
- }
- },
- });
- state.setEntities(entities);
-
- unsubscribe = () => subscription.cancel();
- };
-
- if (address) {
- subscribe(address);
- }
-
- return () => {
- if (unsubscribe) {
- unsubscribe();
- }
- };
- }, [sdk, address]);
-
+ const entityId = useEntityId(address ?? "0");
+ useEntityQuery(
+ new ToriiQueryBuilder()
+ .withClause(
+ KeysClause(
+ [ModelsMapping.Moves, ModelsMapping.Position],
+ [addAddressPadding(address ?? "0")],
+ "VariableLen"
+ ).build()
+ )
+ .includeHashedKeys()
+ );
return entityId;
}
diff --git a/examples/example-vite-react-sql/src/routes/__root.tsx b/examples/example-vite-react-sql/src/routes/__root.tsx
index 81507374..e99f0358 100644
--- a/examples/example-vite-react-sql/src/routes/__root.tsx
+++ b/examples/example-vite-react-sql/src/routes/__root.tsx
@@ -21,6 +21,7 @@ function RootComponent() {
+ {/* @ts-ignore */}
diff --git a/examples/example-vite-react-threejs-recs/src/gameComponents/Player.tsx b/examples/example-vite-react-threejs-recs/src/gameComponents/Player.tsx
index 89206b19..24b400b3 100644
--- a/examples/example-vite-react-threejs-recs/src/gameComponents/Player.tsx
+++ b/examples/example-vite-react-threejs-recs/src/gameComponents/Player.tsx
@@ -140,6 +140,7 @@ export const Player = (props: any) => {
return (
<>
+ {/* @ts-ignore */}
{
const initialCameraPosition = new Vector3(0, 100.0, 100); // Desired initial position
return (
+ // @ts-ignore