name: midnight-dapp:state-management description: 用于读取公共账本状态、实现与链状态同步的响应式UI、缓存状态以优化性能,或理解Midnight DApp中的公共与私有状态。
状态管理
在Midnight DApp中读取、同步和缓存合约状态,并正确处理双状态模型。
使用场景
- 从已部署合约中读取公共账本状态
- 实现与链状态同步更新的响应式UI
- 缓存状态以优化性能
- 理解何时使用公共状态与私有状态
- 跨多个组件同步状态
核心概念
双状态模型
Midnight合约拥有两种状态类型:
| 状态类型 | 存储位置 | 访问方式 | 可见性 |
|---|---|---|---|
| 公共账本状态 | 链上(索引器) | contract.state.* |
任何人都可读取 |
| 私有本地状态 | 浏览器(LevelDB) | WitnessContext.privateState |
仅本地用户 |
这与以太坊所有状态都公开在链上的模式有根本不同。
状态访问模式
// 公共状态 - 任何人都可读取
const totalSupply = await contract.state.total_supply();
const balance = await contract.state.balances.get(address);
// 私有状态 - 仅在见证中访问
const witnesses = {
get_secret: ({ privateState }) => privateState.secretKey,
};
链同步
链上状态可能因其他交易而改变。DApp必须处理:
- 初始加载 - 组件挂载时获取当前状态
- 轮询 - 定期检查更新
- WebSocket - 订阅实时更新
- 乐观更新 - 在确认前更新UI
参考资料
| 文档 | 描述 |
|---|---|
| contract-state.md | 读取公共和私有合约状态 |
| chain-sync.md | 同步模式和订阅 |
| privacy-aware-caching.md | Midnight的安全缓存策略 |
| web3-comparison.md | 以太坊状态模式与Midnight对比 |
示例
| 示例 | 描述 |
|---|---|
| use-contract-state/ | 读取合约状态的React钩子 |
| state-sync-provider/ | 状态同步的上下文提供者 |
| cache-manager/ | 隐私感知缓存工具 |
快速开始
1. 读取简单状态值
// 部署合约后,读取公共状态
const contract = await deployedContract();
// 简单值
const totalSupply: bigint = await contract.state.total_supply();
// 映射查找
const balance: bigint | undefined = await contract.state.balances.get(userAddress);
// 集合成员检查
const isMember: boolean = await contract.state.members.has(userAddress);
2. 创建状态钩子
function useContractState<T>(
contract: Contract,
accessor: (state: ContractState) => Promise<T>
) {
const [value, setValue] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
async function fetchState() {
setLoading(true);
const result = await accessor(contract.state);
if (!cancelled) {
setValue(result);
setLoading(false);
}
}
fetchState();
return () => { cancelled = true; };
}, [contract, accessor]);
return { value, loading };
}
3. 订阅更新
// 轮询状态变化
useEffect(() => {
const interval = setInterval(async () => {
const newBalance = await contract.state.balances.get(address);
setBalance(newBalance);
}, 5000); // 每5秒轮询一次
return () => clearInterval(interval);
}, [contract, address]);
常用模式
类型安全的状态读取
interface ContractState {
total_supply(): Promise<bigint>;
balances: {
get(address: string): Promise<bigint | undefined>;
};
members: {
has(address: string): Promise<boolean>;
};
}
// 编译器从你的Compact合约生成这些类型
错误处理
async function safeStateRead<T>(
accessor: () => Promise<T>,
fallback: T
): Promise<T> {
try {
return await accessor();
} catch (error) {
console.error('状态读取失败:', error);
return fallback;
}
}
const balance = await safeStateRead(
() => contract.state.balances.get(address),
0n
);
乐观更新
// 立即更新UI,然后与链同步
const [balance, setBalance] = useState<bigint>(0n);
async function transfer(to: string, amount: bigint) {
// 乐观更新
setBalance(prev => prev - amount);
try {
await contract.callTx.transfer(to, amount, witnesses);
// 状态将在下次轮询时同步
} catch (error) {
// 回滚乐观更新
setBalance(prev => prev + amount);
throw error;
}
}
相关技能
wallet-integration- 设置提供者所需proof-handling- 访问私有状态的见证实现transaction-flows- 提交状态变更交易error-handling- 处理状态读取错误
相关命令
/dapp-check- 验证提供者配置/dapp-debug state- 调试状态同步问题