State Management Documentation¶
This document describes the state management architecture implemented in the Prediction DAO frontend.
Overview¶
The application uses React Context API with custom hooks to manage global and local state. This approach eliminates prop drilling and provides a clean, testable interface for state management.
Architecture¶
Contexts¶
1. Web3Context (src/contexts/Web3Context.jsx)¶
Manages all Web3-related state including: - Wallet connection (account, isConnected) - Network information (chainId, networkError) - Provider and signer instances - Connection actions (connectWallet, disconnectWallet, switchNetwork)
Provider Setup:
2. UIContext (src/contexts/UIContext.jsx)¶
Manages all UI-related state including: - Notifications (user feedback messages) - Announcements (screen reader accessibility) - Modals (dialog system) - Error handling (global error state)
Provider Setup:
Custom Hooks¶
Web3 Hooks (src/hooks/useWeb3.js)¶
useWeb3()- Access full Web3 contextuseAccount()- Get account and connection stateuseNetwork()- Get network state and switch functionuseEthers()- Get provider and signer instancesuseWallet()- Access wallet connection functions
Example:
import { useWeb3, useAccount, useEthers } from './hooks/useWeb3'
function MyComponent() {
const { account, isConnected } = useAccount()
const { provider, signer } = useEthers()
// Use the values...
}
UI Hooks (src/hooks/useUI.js)¶
useUI()- Access full UI contextuseNotification()- Show/hide notificationsuseAnnouncement()- Make screen reader announcementsuseModal()- Show/hide modalsuseError()- Handle errors globally
Example:
import { useNotification, useAnnouncement } from './hooks/useUI'
function MyComponent() {
const { showNotification } = useNotification()
const { announce } = useAnnouncement()
const handleAction = () => {
showNotification('Action completed!', 'success')
announce('Action completed successfully')
}
}
Blockchain Event Hooks (src/hooks/useBlockchainEvents.js)¶
useContractEvent(contract, eventName, handler, notify)- Listen to single contract eventuseContractEvents(contract, events)- Listen to multiple contract eventsuseAccountChange(callback)- Listen to wallet account changesuseChainChange(callback)- Listen to network changes
Example:
import { useContractEvent } from './hooks/useBlockchainEvents'
function MyComponent() {
const contract = ... // your contract instance
useContractEvent(
contract,
'ProposalSubmitted',
(proposalId, proposer) => {
console.log('New proposal:', proposalId)
// Handle the event
},
true // show notification
)
}
UI Components¶
NotificationSystem (src/components/ui/NotificationSystem.jsx)¶
Displays toast-style notifications with different types:
- info - Information messages (blue)
- success - Success messages (green)
- warning - Warning messages (orange)
- error - Error messages (red)
Auto-dismisses after 5 seconds by default.
ModalSystem (src/components/ui/ModalSystem.jsx)¶
Displays modal dialogs with: - Focus management (traps focus inside modal) - Keyboard support (Escape to close) - Backdrop click to close (configurable) - Accessible (ARIA attributes)
Example:
import { useModal } from './hooks/useUI'
function MyComponent() {
const { showModal, hideModal } = useModal()
const openDialog = () => {
showModal(
<div>
<p>Modal content here</p>
<button onClick={hideModal}>Close</button>
</div>,
{
title: 'My Dialog',
size: 'medium',
closable: true
}
)
}
}
AnnouncementRegion (src/components/ui/AnnouncementRegion.jsx)¶
Hidden region for screen reader announcements. Automatically announces important state changes for accessibility.
ErrorBoundary (src/components/ui/ErrorBoundary.jsx)¶
Catches React errors and displays a fallback UI. Prevents entire app crashes from component errors.
Usage Patterns¶
1. Removing Prop Drilling¶
Before:
function App() {
const [account, setAccount] = useState(null)
return <Dashboard account={account} />
}
function Dashboard({ account }) {
return <UserProfile account={account} />
}
function UserProfile({ account }) {
return <div>{account}</div>
}
After:
function App() {
return <Dashboard />
}
function Dashboard() {
return <UserProfile />
}
function UserProfile() {
const { account } = useAccount()
return <div>{account}</div>
}
2. Transaction Feedback¶
import { useNotification, useAnnouncement } from './hooks/useUI'
function TransactionButton() {
const { showNotification } = useNotification()
const { announce } = useAnnouncement()
const handleTransaction = async () => {
try {
showNotification('Transaction submitted...', 'info', 0)
announce('Transaction submitted')
const tx = await contract.submitTransaction()
const receipt = await tx.wait()
showNotification('Transaction successful!', 'success')
announce('Transaction completed successfully')
} catch (error) {
showNotification('Transaction failed', 'error')
announce('Transaction failed')
}
}
return <button onClick={handleTransaction}>Submit</button>
}
3. Real-time Event Listening¶
import { useContractEvents } from './hooks/useBlockchainEvents'
function ProposalList() {
const contract = ... // your contract
useContractEvents(contract, [
{
name: 'ProposalSubmitted',
handler: (id, proposer) => {
console.log('New proposal:', id)
refreshProposals()
},
notify: true,
message: 'New proposal submitted!'
},
{
name: 'VoteCast',
handler: (voter, proposalId) => {
console.log('Vote cast:', proposalId)
refreshVotes()
},
notify: true,
message: 'Vote cast on proposal!'
}
])
return <div>...</div>
}
Testing¶
State Persistence¶
State is maintained across: - Navigation between routes - Component unmounting/remounting - Network changes - Account changes
Event Responsiveness¶
The system responds to: - Blockchain events (contract events) - Wallet events (account/network changes) - User interactions (clicks, form submissions) - Transaction lifecycle (submitted, confirmed, failed)
Accessibility¶
All state management components follow WCAG 2.1 AA guidelines:
- Notifications: Proper ARIA live regions (
politeorassertive) - Announcements: Screen reader friendly announcements for state changes
- Modals: Focus trapping, keyboard navigation, ARIA attributes
- Error Boundaries: Accessible error messages and recovery options
Best Practices¶
-
Use the right hook: Use specific hooks (
useAccount,useNotification) instead of generic ones (useWeb3,useUI) when you only need specific functionality. -
Avoid unnecessary re-renders: Hooks use memoization internally, but be mindful of creating new callback functions in render.
-
Clean up listeners: Event listeners are automatically cleaned up, but be aware of potential memory leaks with long-running subscriptions.
-
Error handling: Always wrap async operations in try-catch and provide user feedback.
-
Accessibility first: Always use both notifications (visual) and announcements (screen reader) for important state changes.
File Structure¶
frontend/src/
├── contexts/
│ ├── Web3Context.jsx # Web3 state management
│ ├── UIContext.jsx # UI state management
│ └── index.js # Context exports
├── hooks/
│ ├── useWeb3.js # Web3 hooks
│ ├── useUI.js # UI hooks
│ ├── useBlockchainEvents.js # Event listener hooks
│ └── index.js # Hook exports
└── components/
└── ui/
├── NotificationSystem.jsx
├── ModalSystem.jsx
├── AnnouncementRegion.jsx
└── ErrorBoundary.jsx
Migration Guide¶
To migrate existing components:
- Remove props for
account,provider,signer,networkError - Import and use appropriate hooks (
useAccount,useEthers, etc.) - Replace inline state management with context hooks
- Add notification/announcement calls for user feedback
- Test component still works without prop drilling
Example: