UNPKG

@msquared/etherbase-client

Version:

React hooks for interacting with Etherbase smart contracts

102 lines (101 loc) 3.97 kB
"use client"; import { useEffect, useMemo, useRef, useState } from "react"; import { useEtherbaseContext } from "../EtherbaseProvider"; import { getConfig } from "../config"; import { WebSocketManager } from "./WebSocketManager"; import useEtherbaseSource from "./useEtherbaseSource"; function deepMerge(target, source) { if (!target) { return source; } if (!source) { return target || {}; } const output = { ...target }; for (const key in source) { console.log("key", key, source[key], target[key]); if (source[key] === null) { delete output[key]; } else if (source[key] instanceof Object && key in target) { output[key] = deepMerge(target[key], source[key]); } else { output[key] = source[key]; } } return output; } export default function useEtherstore({ contractAddress, path, options = {}, onStateChange, }) { useEtherbaseContext(); const [state, setState] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const keyRef = useRef(Math.random().toString(36).substring(2, 9)); if (!contractAddress) { throw new Error("Contract address must be provided in path"); } // biome-ignore lint/correctness/useExhaustiveDependencies: need a stable reference const statePath = useMemo(() => path, [JSON.stringify(path)]); // biome-ignore lint/correctness/useExhaustiveDependencies: need a stable reference const optionsMemo = useMemo(() => options, [JSON.stringify(options)]); const { useBackend } = getConfig(); const { setValue } = useEtherbaseSource({ sourceAddress: contractAddress }); // state is the new state to be set. todo implement merge false const update = async (state, merge = true) => { // todo check if the contract is a source // if (!isSource(contractAddress)) { // throw new Error(`Contract ${contractAddress} is not registered as an source`); // } if (!useBackend) { await setValue(state); return; } console.log("Updating state", contractAddress, state); try { await WebSocketManager.get().setValue({ contractAddress, state, }); } catch (err) { setError(err instanceof Error ? err.message : String(err)); throw err; } }; useEffect(() => { async function createSubscription() { try { await WebSocketManager.get().addStateSubscription(keyRef.current, { contractAddress: contractAddress, statePath: statePath, options: optionsMemo, onStateUpdate: (update) => { setLoading(false); // deep merge the update into the state // @ts-ignore setState((prev) => { const mergedState = deepMerge(prev, update.state); if (onStateChange) { onStateChange(mergedState); } return mergedState; }); }, }); } catch (err) { console.error("Error creating subscription", err); setError(err instanceof Error ? err.message : String(err)); setLoading(false); return; } } console.log("Creating subscription", contractAddress, statePath); createSubscription(); return () => { WebSocketManager.get().removeSubscription(keyRef.current); }; }, [contractAddress, statePath, optionsMemo, onStateChange]); return { state, loading, error, update }; }