@defai/widget-react
v1.0.0
Published
React hooks and components for DEFAI widgets
Readme
@defai/widget-react
React hooks and components for building DEFAI widgets.
Installation
npm install @defai/widget-react @defai/widget-sdk reactQuick Start
import React from 'react';
import {
WidgetProvider,
useWidgetContext,
useWidgetAPI,
useWidgetState
} from '@defai/widget-react';
function MyWidgetComponent() {
const context = useWidgetContext();
const api = useWidgetAPI();
const [state, setState] = useWidgetState({
counter: 0
});
const handleIncrement = () => {
setState({ counter: state.counter + 1 });
};
return (
<div>
<h1>Count: {state.counter}</h1>
<button onClick={handleIncrement}>Increment</button>
<p>Widget ID: {context.widgetId}</p>
</div>
);
}
// In your widget class
export class MyWidget extends DefaiWidget {
async onMount(context: WidgetContext): Promise<void> {
const root = document.getElementById('widget-root');
ReactDOM.render(
<WidgetProvider context={context}>
<MyWidgetComponent />
</WidgetProvider>,
root
);
}
}Hooks
useWidgetContext()
Access the full widget context.
const context = useWidgetContext();
console.log(context.widgetId, context.userTier, context.theme);useWidgetAPI()
Access widget APIs directly.
const api = useWidgetAPI();
// Use storage
await api.storage.set('key', 'value');
const value = await api.storage.get('key');
// Check wallet
if (api.wallet.isConnected()) {
const balance = await api.wallet.getBalance();
}useWidgetState<T>(initialState)
Manage widget state with React hooks.
const [state, setState] = useWidgetState({
isLoading: false,
data: null,
error: null
});
// Update state
setState({ isLoading: true });
// Partial updates
setState({ data: newData });useWidgetEvents()
Handle widget events easily.
const { emit, on } = useWidgetEvents();
// Emit events
const handleClick = () => {
emit('button-clicked', { timestamp: Date.now() });
};
// Listen to events
useEffect(() => {
const unsubscribe = on('external-data', (data) => {
console.log('Received:', data);
});
return unsubscribe;
}, []);useWidgetSize()
Responsive design based on widget size.
const { width, height, isCompact } = useWidgetSize();
return (
<div className={isCompact ? 'compact-view' : 'full-view'}>
{isCompact ? <CompactLayout /> : <FullLayout />}
</div>
);useWidgetTheme()
Access and respond to theme changes.
const theme = useWidgetTheme();
return (
<div className={theme === 'dark' ? 'dark-mode' : 'light-mode'}>
{/* Your content */}
</div>
);useWidgetStorage<T>(key, defaultValue)
Persistent storage with React state.
const [settings, saveSettings, clearSettings] = useWidgetStorage('settings', {
notifications: true,
autoRefresh: false
});
const toggleNotifications = async () => {
await saveSettings({
...settings,
notifications: !settings.notifications
});
};useWidgetWallet()
Wallet connection and balance tracking.
const { connected, address, balance } = useWidgetWallet();
if (!connected) {
return <div>Please connect your wallet</div>;
}
return (
<div>
<p>Address: {address}</p>
<p>Balance: {balance} SOL</p>
</div>
);useWidgetPrices(symbols)
Real-time price subscriptions.
const { prices, loading } = useWidgetPrices(['SOL', 'BTC', 'ETH']);
if (loading) return <div>Loading prices...</div>;
return (
<div>
{Object.entries(prices).map(([symbol, price]) => (
<div key={symbol}>{symbol}: ${price}</div>
))}
</div>
);Components
<WidgetProvider>
Provides widget context to child components.
<WidgetProvider context={widgetContext}>
<App />
</WidgetProvider><WidgetErrorBoundary>
Catch and display widget errors gracefully.
<WidgetErrorBoundary fallback={<ErrorFallback />}>
<YourWidget />
</WidgetErrorBoundary><WidgetLoader>
Loading state component.
if (isLoading) {
return <WidgetLoader message="Loading data..." />;
}<WidgetPermissionCheck>
Conditionally render based on permissions.
<WidgetPermissionCheck permission="wallet" fallback={<NoWalletAccess />}>
<WalletFeatures />
</WidgetPermissionCheck>Advanced Usage
Custom Hooks
Create custom hooks for your widget logic:
function useTokenPair(token1: string, token2: string) {
const api = useWidgetAPI();
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const [price1, price2] = await Promise.all([
api.prices.get(token1),
api.prices.get(token2)
]);
setData({
[token1]: price1,
[token2]: price2,
ratio: price1 / price2
});
};
fetchData();
const interval = setInterval(fetchData, 5000);
return () => clearInterval(interval);
}, [token1, token2]);
return data;
}TypeScript Support
Full TypeScript support with type inference:
interface MyWidgetState {
counter: number;
items: string[];
settings: {
theme: 'light' | 'dark';
refreshRate: number;
};
}
const [state, setState] = useWidgetState<MyWidgetState>({
counter: 0,
items: [],
settings: {
theme: 'dark',
refreshRate: 5000
}
});Best Practices
- Always wrap your app with
WidgetProvider - Use error boundaries to handle component errors
- Clean up subscriptions in useEffect returns
- Memoize expensive computations with useMemo
- Handle loading states for async operations
- Check permissions before using restricted APIs
- Optimize re-renders with React.memo
License
MIT
