/** * Cloudflare KV Demo Component * * This component demonstrates how to use the Cloudflare KV utilities * in a React application with proper loading states and error handling. */ import React, { useState } from 'react' import { useCloudflareKV, useKVValue } from '../hooks/useCloudflareKV' interface DemoData { message: string timestamp: string counter: number } export const CloudflareKVDemo: React.FC = () => { const [inputKey, setInputKey] = useState('demo:test') const [inputValue, setInputValue] = useState('Hello, Cloudflare KV!') const [searchPrefix, setSearchPrefix] = useState('demo:') const [logs, setLogs] = useState([]) // Add log function const addLog = (message: string) => { const timestamp = new Date().toLocaleTimeString() const logMessage = `[${timestamp}] ${message}` console.log(logMessage) setLogs(prev => [logMessage, ...prev].slice(0, 20)) // Keep last 20 logs } // Use the general KV hook const kv = useCloudflareKV() // Use the specific value hook for a counter const { value: counterValue, loading: counterLoading, error: counterError, save: saveCounter, refresh: refreshCounter } = useKVValue('demo:counter', true, true) // Log initial state React.useEffect(() => { addLog('CloudflareKVDemo component initialized') addLog(`KV loading state: ${kv.isLoading}`) addLog(`KV has error: ${kv.hasError}`) if (kv.hasError) { addLog(`KV errors: ${JSON.stringify(kv.errors)}`) } }, [kv.isLoading, kv.hasError, kv.errors]) // Handle putting a value const handlePutValue = async () => { if (!inputKey || !inputValue) { addLog('❌ Put operation failed: Key or value is empty') alert('❌ Please enter both key and value') return } addLog(`🔄 Starting put operation for key: ${inputKey}`) try { const data: DemoData = { message: inputValue, timestamp: new Date().toISOString(), counter: (typeof counterValue === 'number' ? counterValue : 0) + 1 } addLog(`📤 Sending data: ${JSON.stringify(data)}`) const result = await kv.put(inputKey, data) addLog(`✅ Put operation successful: ${JSON.stringify(result)}`) alert('✅ Value stored successfully!') } catch (error) { const errorMessage = (error as Error).message addLog(`❌ Put operation failed: ${errorMessage}`) alert('❌ Failed to store value: ' + errorMessage) } } // Handle getting a value const handleGetValue = async () => { if (!inputKey) { addLog('❌ Get operation failed: Key is empty') alert('❌ Please enter a key') return } addLog(`🔄 Starting get operation for key: ${inputKey}`) try { const result = await kv.get(inputKey) if (result) { addLog(`✅ Get operation successful: ${JSON.stringify(result)}`) alert(`✅ Retrieved value: ${JSON.stringify(result, null, 2)}`) } else { addLog(`â„šī¸ Get operation completed: Key not found`) alert('â„šī¸ Key not found') } } catch (error) { const errorMessage = (error as Error).message addLog(`❌ Get operation failed: ${errorMessage}`) alert('❌ Failed to get value: ' + errorMessage) } } // Handle deleting a value const handleDeleteValue = async () => { if (!inputKey) { addLog('❌ Delete operation failed: Key is empty') alert('❌ Please enter a key') return } addLog(`🔄 Starting delete operation for key: ${inputKey}`) try { const result = await kv.delete(inputKey) addLog(`✅ Delete operation successful: ${JSON.stringify(result)}`) alert('✅ Value deleted successfully!') } catch (error) { const errorMessage = (error as Error).message addLog(`❌ Delete operation failed: ${errorMessage}`) alert('❌ Failed to delete value: ' + errorMessage) } } // Handle listing keys const handleListKeys = async () => { addLog(`🔄 Starting list keys operation with prefix: ${searchPrefix}`) try { const result = await kv.listKeys(searchPrefix, 50) addLog(`✅ List keys operation successful: Found ${result.result.keys.length} keys`) const keyNames = result.result.keys.map(k => k.name).join('\n') alert(`✅ Found keys:\n${keyNames || 'No keys found'}`) } catch (error) { const errorMessage = (error as Error).message addLog(`❌ List keys operation failed: ${errorMessage}`) alert('❌ Failed to list keys: ' + errorMessage) } } // Handle incrementing counter const handleIncrementCounter = async () => { try { const newValue = (typeof counterValue === 'number' ? counterValue : 0) + 1 await saveCounter(newValue) } catch (error) { alert('❌ Failed to increment counter: ' + (error as Error).message) } } // Handle batch operations const handleBatchPut = async () => { try { const entries = [ { key: 'batch:item1', value: { name: 'Item 1', price: 10.99 } }, { key: 'batch:item2', value: { name: 'Item 2', price: 15.50 } }, { key: 'batch:item3', value: { name: 'Item 3', price: 8.75 } } ] await kv.putBatch(entries) alert('✅ Batch operation completed!') } catch (error) { alert('❌ Batch operation failed: ' + (error as Error).message) } } return (

Cloudflare KV Demo

{/* Loading indicator */} {kv.isLoading && (
Processing...
)} {/* Error display */} {kv.hasError && (

Errors:

    {kv.errors.get &&
  • Get: {kv.errors.get}
  • } {kv.errors.put &&
  • Put: {kv.errors.put}
  • } {kv.errors.delete &&
  • Delete: {kv.errors.delete}
  • } {kv.errors.list &&
  • List: {kv.errors.list}
  • }
)} {/* Counter Section */}

Auto-loaded Counter

Current value: {counterLoading ? '...' : counterValue || 0}
{counterError && (

{counterError}

)}
{/* Manual Operations Section */}

Manual Operations

{/* Key-Value Input */}
setInputKey(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Enter key name" />
setInputValue(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Enter value" />
{/* Action Buttons */}
{/* List Keys Section */}
setSearchPrefix(e.target.value)} className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Enter prefix to search" />
{/* Batch Operations */}
{/* Clear States */}
{/* Debug Logs Section */}

Debug Logs

{logs.length === 0 ? (
No logs yet...
) : ( logs.map((log, index) => (
{log}
)) )}
) } export default CloudflareKVDemo