diff --git a/templates/react/packages/client/src/App.tsx b/templates/react/packages/client/src/App.tsx index 1cea1cc84e..a025d67b3e 100644 --- a/templates/react/packages/client/src/App.tsx +++ b/templates/react/packages/client/src/App.tsx @@ -3,12 +3,17 @@ import { ConnectButton } from "@rainbow-me/rainbowkit"; import { useWalletClient, useAccount } from "wagmi"; import { useComponentValue } from "@latticexyz/react"; import { useMUD } from "./MUDContext"; +import { setup } from "./mud/setup"; import { singletonEntity } from "@latticexyz/store-sync/recs"; -import { useSystemCalls } from "./mud/useSystemCalls"; -import { useDelegationControl } from "./mud/useDelegationControl"; +import { useSystemCalls } from "./hooks/useSystemCalls"; +import { useDelegationControl } from "./hooks/useDelegationControl"; -export const App = () => { - const { network, components } = useMUD(); +type Props = { + setup: Awaited>; +}; + +export const App = ({ setup }: Props) => { + const { network, components } = setup; // TODO rename to delegatee const { walletClient: burnerWalletClient } = network; @@ -20,8 +25,6 @@ export const App = () => { const delegationControlId = useDelegationControl(walletClient.data, burnerWalletClient, components); const counter = useComponentValue(Counter, singletonEntity); - console.log({ account, delegationControlId }); - return ( <>
diff --git a/templates/react/packages/client/src/MUDContext.tsx b/templates/react/packages/client/src/MUDContext.tsx index 8747188be0..6568ef6312 100644 --- a/templates/react/packages/client/src/MUDContext.tsx +++ b/templates/react/packages/client/src/MUDContext.tsx @@ -1,4 +1,4 @@ -import { createContext, ReactNode, useContext, useState, useEffect } from "react"; +import { createContext, ReactNode, useContext, useState, useEffect, useMemo } from "react"; import { setup, SetupResult } from "./mud/setup"; import { usePromise } from "@latticexyz/react"; @@ -8,27 +8,35 @@ type Props = { children: ReactNode; }; +// THis is not used. Context doesn't play well with async data fetch so used hook instead export const MUDProvider = ({ children }: Props) => { const currentValue = useContext(MUDContext); if (currentValue) throw new Error("MUDProvider can only be used once"); + // Had weird re-rendering issue with the approach below // const setupResult = usePromise(setup()); // if (setupResult.status === "rejected") throw new Error("Ecountered error while setting up MUD"); // if (setupResult.status !== "fulfilled") return; + const setupPromise = useMemo(() => { + return setup(); + }, []); const [setupResult, setSetupResult] = useState> | null>(null); useEffect(() => { - setup().then((response) => setSetupResult(response)); - }, []); + setupPromise.then((result) => setSetupResult(result)); + + return () => { + setupPromise.then((result) => result.network.world.dispose()); + }; + }, [setupPromise]); - return setupResult ? {children} : null; + return {children}; }; export const useMUD = () => { const value = useContext(MUDContext); - if (!value) throw new Error("Must be used within a MUDProvider"); return value; }; diff --git a/templates/react/packages/client/src/mud/useDelegationControl.ts b/templates/react/packages/client/src/hooks/useDelegationControl.ts similarity index 81% rename from templates/react/packages/client/src/mud/useDelegationControl.ts rename to templates/react/packages/client/src/hooks/useDelegationControl.ts index c98b56073d..5d157e8aa7 100644 --- a/templates/react/packages/client/src/mud/useDelegationControl.ts +++ b/templates/react/packages/client/src/hooks/useDelegationControl.ts @@ -2,7 +2,7 @@ import { ComponentValue, Type, getComponentValue } from "@latticexyz/recs"; import { useEffect, useState } from "react"; import { WalletClient, Transport, Chain, Account } from "viem"; import { encodeEntity } from "@latticexyz/store-sync/recs"; -import { ClientComponents } from "./createClientComponents"; +import { ClientComponents } from "../mud/createClientComponents"; export function useDelegationControl( delegator: WalletClient | null | undefined, @@ -32,13 +32,6 @@ export function useDelegationControl( }); const delegationControlId = getComponentValue(components.Delegations, delegationsKeyEntity); - console.log({ - delegationsKeyEntity, - delegator: delegator.account.address, - delegatee: delegatee.account.address, - delegationControlId, - delegations: [...components.Delegations.entities()], - }); setDelegationControlId(delegationControlId); // TODO what's the cleanup fn? diff --git a/templates/react/packages/client/src/hooks/usePromiseValue.ts b/templates/react/packages/client/src/hooks/usePromiseValue.ts new file mode 100644 index 0000000000..bd71492d14 --- /dev/null +++ b/templates/react/packages/client/src/hooks/usePromiseValue.ts @@ -0,0 +1,24 @@ +import { useEffect, useState, useRef } from "react"; + +export const usePromiseValue = (promise: Promise | null) => { + const promiseRef = useRef(promise); + const [value, setValue] = useState(null); + useEffect(() => { + if (!promise) return; + let isMounted = true; + promiseRef.current = promise; + // TODO: do something with promise errors? + promise.then((resolvedValue) => { + // skip if unmounted (state changes will cause errors otherwise) + if (!isMounted) return; + // If our promise was replaced before it resolved, ignore the result + if (promiseRef.current !== promise) return; + + setValue(resolvedValue); + }); + return () => { + isMounted = false; + }; + }, [promise]); + return value; +}; diff --git a/templates/react/packages/client/src/hooks/useSetup.tsx b/templates/react/packages/client/src/hooks/useSetup.tsx new file mode 100644 index 0000000000..8f11d8c765 --- /dev/null +++ b/templates/react/packages/client/src/hooks/useSetup.tsx @@ -0,0 +1,17 @@ +import { useEffect, useMemo } from "react"; +import { usePromiseValue } from "./usePromiseValue"; +import { setup } from "../mud/setup"; + +export const useSetup = () => { + const setupPromise = useMemo(() => { + return setup(); + }, []); + + useEffect(() => { + return () => { + setupPromise.then((result) => result.network.world.dispose()); + }; + }, [setupPromise]); + + return usePromiseValue(setupPromise); +}; diff --git a/templates/react/packages/client/src/mud/useSystemCalls.ts b/templates/react/packages/client/src/hooks/useSystemCalls.ts similarity index 77% rename from templates/react/packages/client/src/mud/useSystemCalls.ts rename to templates/react/packages/client/src/hooks/useSystemCalls.ts index 23a7db014e..7046b91f11 100644 --- a/templates/react/packages/client/src/mud/useSystemCalls.ts +++ b/templates/react/packages/client/src/hooks/useSystemCalls.ts @@ -1,8 +1,8 @@ import { useEffect, useState } from "react"; import { WalletClient, Transport, Chain, Account } from "viem"; -import { createSystemCalls } from "./createSystemCalls"; -import { SetupNetworkResult } from "./setupNetwork"; -import { ClientComponents } from "./createClientComponents"; +import { createSystemCalls } from "../mud/createSystemCalls"; +import { SetupNetworkResult } from "../mud/setupNetwork"; +import { ClientComponents } from "../mud/createClientComponents"; export function useSystemCalls( network: SetupNetworkResult, diff --git a/templates/react/packages/client/src/index.tsx b/templates/react/packages/client/src/index.tsx index 52bd9c62e0..8703e405d6 100644 --- a/templates/react/packages/client/src/index.tsx +++ b/templates/react/packages/client/src/index.tsx @@ -6,43 +6,40 @@ import mudConfig from "contracts/mud.config"; import IWorldAbi from "contracts/out/IWorld.sol/IWorld.abi.json"; import { App } from "./App"; import { MUDProvider, useMUD } from "./MUDContext"; +import { useSetup } from "./hooks/useSetup"; const rootElement = document.getElementById("react-root"); if (!rootElement) throw new Error("React root not found"); const root = ReactDOM.createRoot(rootElement); const AppWithWalletContext = () => { - const { network } = useMUD(); - const { wagmiConfig, chain } = network; + const setup = useSetup(); // https://vitejs.dev/guide/env-and-mode.html - if (import.meta.env.DEV) { + if (import.meta.env.DEV && setup) { import("@latticexyz/dev-tools").then(({ mount: mountDevTools }) => mountDevTools({ config: mudConfig, - publicClient: network.publicClient, - walletClient: network.walletClient, - latestBlock$: network.latestBlock$, - storedBlockLogs$: network.storedBlockLogs$, - worldAddress: network.worldAddress, + publicClient: setup.network.publicClient, + walletClient: setup.network.walletClient, + latestBlock$: setup.network.latestBlock$, + storedBlockLogs$: setup.network.storedBlockLogs$, + worldAddress: setup.network.worldAddress, worldAbi: IWorldAbi, - write$: network.write$.asObservable().pipe(share()), - recsWorld: network.world, + write$: setup.network.write$.asObservable().pipe(share()), + recsWorld: setup.network.world, }) ); } + if (!setup) return <>Loading...; return ( - - - + + + ); }; -root.render( - - - -); +root.render(); diff --git a/templates/react/packages/client/src/mud/createSystemCalls.ts b/templates/react/packages/client/src/mud/createSystemCalls.ts index 3a589c8cc7..a904cfa4cc 100644 --- a/templates/react/packages/client/src/mud/createSystemCalls.ts +++ b/templates/react/packages/client/src/mud/createSystemCalls.ts @@ -42,9 +42,9 @@ export function createSystemCalls( * is in the root namespace, `.increment` can be called directly * on the World contract. */ - const tx = await worldContract.write.increment({ - gas: 15000000n, - }); + // gas added here is a workaround for intrinsic gas too high error + // likely something along with commenting out the defaultPriorityFee on chain config? + const tx = await worldContract.write.increment({ gas: 1500000 }); await waitForTransaction(tx); return getComponentValue(Counter, singletonEntity); }; @@ -59,10 +59,7 @@ export function createSystemCalls( getResourceSelector, }); const callData = "0x"; - console.log({ delegatee, UNLIMITED_DELEGATION, callData }); - const tx = await contractWithDelegator.write.registerDelegation([delegatee, UNLIMITED_DELEGATION, callData], { - gas: 15000000n, - }); + const tx = await contractWithDelegator.write.registerDelegation([delegatee, UNLIMITED_DELEGATION, callData]); await waitForTransaction(tx); };