← スキル一覧に戻る

dojo-react
by dojoengine
Official JavaScript packages for Dojo.
⭐ 37🍴 55📅 2026年1月23日
SKILL.md
name: dojo-react description: | Use for React integration patterns and best practices in dojo.js. Triggers: dojo react, react hooks, effect atoms, Result.match, infinite scroll dojo, DojoSdkProvider, useDojoSDK, react patterns
Dojo.js React Patterns
When to Use
Use this skill when:
- Setting up React provider hierarchy
- Using Effect atoms for complex async state
- Implementing infinite scroll
- Handling loading/error states
Provider Hierarchy
Recommended order:
import { StarknetConfig } from "@starknet-react/core";
import { DojoSdkProvider } from "@dojoengine/sdk/react";
function Providers({ children }) {
return (
<StarknetConfig connectors={connectors}>
<DojoSdkProvider
dojoConfig={dojoConfig}
sdk={sdk}
clientFn={createClient}
>
{children}
</DojoSdkProvider>
</StarknetConfig>
);
}
useDojoSDK Hook
import { useDojoSDK } from "@dojoengine/sdk/react";
function GameComponent() {
const {
sdk, // SDK instance
config, // DojoConfig
client, // Client from clientFn
provider, // DojoProvider
useDojoStore // Zustand store hook
} = useDojoSDK();
}
Effect Atoms (Recommended)
Effect atoms provide robust async state management:
Entity Query Atom
import { createEntityQueryAtom } from "@dojoengine/react/effect";
import { Result } from "@dojoengine/react/effect";
const playersAtom = createEntityQueryAtom(
runtime,
new ToriiQueryBuilder().addEntityModel("game-Player").withLimit(100)
);
function PlayerList() {
const players = useAtomValue(playersAtom);
return Result.match(players, {
onSuccess: ({ value }) => (
<ul>
{value.items.map(p => <li key={p.entityId}>{p.models.game.Player.name}</li>)}
</ul>
),
onFailure: (error) => <div>Error: {error.message}</div>,
onInitial: () => <div>Loading...</div>
});
}
Entity Updates Atom (Real-time)
import { createEntityUpdatesAtom } from "@dojoengine/react/effect";
const updatesAtom = createEntityUpdatesAtom(
runtime,
KeysClause(["game-Player"], [], "VariableLen").build()
);
Combined Query + Updates
import { createEntityQueryWithUpdatesAtom } from "@dojoengine/react/effect";
const livePlayersAtom = createEntityQueryWithUpdatesAtom(
runtime,
query,
clause
);
Infinite Scroll
import { createEntitiesInfiniteScrollAtom } from "@dojoengine/react/effect";
const infinitePlayersAtom = createEntitiesInfiniteScrollAtom(
runtime,
new ToriiQueryBuilder().addEntityModel("game-Player"),
20 // page size
);
function InfinitePlayerList() {
const [state, loadMore] = useAtom(infinitePlayersAtom);
return Result.match(state, {
onSuccess: ({ value }) => (
<>
<ul>
{value.items.map(p => (
<li key={p.entityId}>{p.models.game.Player.name}</li>
))}
</ul>
{value.hasMore && (
<button onClick={loadMore}>Load More</button>
)}
</>
),
onFailure: (error) => <div>Error: {error.message}</div>,
onInitial: () => <div>Loading...</div>
});
}
Token Balance Atoms
import {
createTokenBalanceQueryAtom,
createTokenBalanceUpdatesAtom
} from "@dojoengine/react/effect";
// One-time query
const balanceAtom = createTokenBalanceQueryAtom(runtime, {
contractAddresses: ["0x..."],
accountAddresses: [playerAddress]
});
// Polling updates
const liveBalanceAtom = createTokenBalanceUpdatesAtom(
runtime,
{ contractAddresses: ["0x..."], accountAddresses: [playerAddress] },
5000 // poll every 5 seconds
);
Data Formatters
Transform data before rendering:
const formatters = {
models: {
"game-Player": (player) => ({
...player,
displayName: player.name || "Anonymous",
displayScore: `${player.score.toLocaleString()} pts`
})
},
fields: {
"game-Player.score": (score) => Math.floor(score / 100)
}
};
const playersAtom = createEntityQueryAtom(runtime, query, formatters);
Custom Subscription Hooks
import { createSubscriptionHook } from "@dojoengine/sdk/react";
const usePlayerSubscription = createSubscriptionHook({
subscribeMethod: (sdk, params) => sdk.subscribeEntityQuery(params),
processInitialData: (data) => data.items,
processUpdateData: (update) => update
});
function PlayerTracker({ entityId }) {
const { data, error, isLoading } = usePlayerSubscription({
query: new ToriiQueryBuilder()
.withClause(KeysClause(["game-Player"], [entityId]).build())
});
}
Zustand Selectors
Optimize re-renders with selectors:
function PlayerScore({ entityId }) {
const { useDojoStore } = useDojoSDK();
// Only re-render when score changes
const score = useDojoStore(
state => state.entities[entityId]?.models?.game?.Player?.score,
(a, b) => a === b // equality function
);
return <span>{score}</span>;
}
useEntityId Hook
import { useEntityId } from "@dojoengine/sdk/react";
function PlayerCard({ address, gameId }) {
// Memoized entity ID computation
const entityId = useEntityId(address, gameId);
const player = useModel(entityId, "game-Player");
}
Error Boundaries
import { ErrorBoundary } from "react-error-boundary";
function GameErrorFallback({ error, resetErrorBoundary }) {
return (
<div>
<p>Something went wrong: {error.message}</p>
<button onClick={resetErrorBoundary}>Retry</button>
</div>
);
}
function App() {
return (
<ErrorBoundary FallbackComponent={GameErrorFallback}>
<Game />
</ErrorBoundary>
);
}
Common Pitfalls
- Missing provider: Ensure DojoSdkProvider wraps all Dojo-using components
- Effect runtime: Create runtime once at app startup, not in components
- Selector stability: Use stable selector functions to prevent re-renders
- Cleanup: Effect atoms handle cleanup, but manual subscriptions need cleanup
- Result.match: Always handle all three cases (success, failure, initial)
スコア
総合スコア
70/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
✓最近の活動
1ヶ月以内に更新
+10
✓フォーク
10回以上フォークされている
+5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
✓タグ
1つ以上のタグが設定されている
+5
レビュー
💬
レビュー機能は近日公開予定です
