npm package: @jim4565/dapp-wrapper-react
Description: React components and hooks for @jim4565/dapp-wrapper with persistent wallet connection
Author: jim4565
Latest version: 1.4.1

# @jim4565/dapp-wrapper-react

🚀 **React components and hooks for [@jim4565/dapp-wrapper](https://github.com/jim4565/dapp-wrapper) with persistent wallet connection**

Built with **industry standards** following **wagmi/RainbowKit patterns** and **MetaMask-level persistence**.

[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
[![React](https://img.shields.io/badge/React-18+-green.svg)](https://reactjs.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## 🎯 What This Library Does

This library **extends** your existing `@jim4565/dapp-wrapper` with:

✅ **Persistent Wallet Connection** - Auto-reconnect after page refresh without popups  
✅ **React Integration** - Hooks, context, and components for seamless React usage  
✅ **Cross-Page Persistence** - Maintain connection across routes and page refreshes  
✅ **3-Tier Reconnection Strategy** - Silent → Session → Full Connect (industry standard)  
✅ **Your Familiar API** - Keep using `contract.playGame(true).withValue("0.1")` syntax  
✅ **Real-time Balance** - Automatic balance updates with 5-strategy fallback system  

**Key Benefit:** Your existing contract interaction code works exactly the same, but now with React hooks and persistent connection!

---

## 🚀 Installation & Setup

### 1. Install the Package

```bash
npm install @jim4565/dapp-wrapper-react @jim4565/dapp-wrapper react react-dom
```

### 2. Critical Setup - Provider Configuration

**⚠️ IMPORTANT:** The provider MUST wrap your entire app to enable persistent connection across all pages.

#### Next.js Setup (`pages/_app.tsx` or `app/layout.tsx`)

```tsx
// pages/_app.tsx (Pages Router)
import { IncentivWalletProvider } from '@jim4565/dapp-wrapper-react';
import type { AppProps } from 'next/app';

// Your existing contract ABIs
const gameABI = [
  {
    "inputs": [{"internalType": "bool", "name": "_isActive", "type": "bool"}],
    "name": "playGame",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  }
  // ... rest of your ABI
];

const contracts = [
  { name: "Game", address: "0x1234567890...", abi: gameABI },
  { name: "Lottery", address: "0x0987654321...", abi: lotteryABI }
];

export default function App({ Component, pageProps }: AppProps) {
  return (
    <IncentivWalletProvider
      config={{
        contracts,              // Register your contracts
        autoConnect: true,      // Auto-reconnect on app start
        reconnectOnMount: true, // Try reconnection when mounted
        pollingInterval: 30000  // Balance polling (30s)
      }}
      enableLogging={process.env.NODE_ENV === 'development'}
    >
      {/* Your entire app - ALL pages will have persistent wallet connection */}
      <Component {...pageProps} />
    </IncentivWalletProvider>
  );
}
```

```tsx
// app/layout.tsx (App Router)
'use client';
import { IncentivWalletProvider } from '@jim4565/dapp-wrapper-react';

const contracts = [
  { name: "Game", address: "0x1234567890...", abi: gameABI }
];

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <IncentivWalletProvider
          config={{
            contracts,
            autoConnect: true,
            reconnectOnMount: true,
            pollingInterval: 30000
          }}
          enableLogging={process.env.NODE_ENV === 'development'}
        >
          {/* All pages and components will have persistent wallet connection */}
          {children}
        </IncentivWalletProvider>
      </body>
    </html>
  );
}
```

#### React Router Setup (`src/App.tsx`)

```tsx
// src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { IncentivWalletProvider } from '@jim4565/dapp-wrapper-react';
import { HomePage } from './pages/HomePage';
import { GamePage } from './pages/GamePage';
import { LeaderboardPage } from './pages/LeaderboardPage';

const contracts = [
  { name: "Game", address: "0x1234567890...", abi: gameABI }
];

function App() {
  return (
    <IncentivWalletProvider 
      config={{ 
        contracts,
        autoConnect: true,
        reconnectOnMount: true,
        pollingInterval: 30000
      }}
      enableLogging={process.env.NODE_ENV === 'development'}
    >
      <BrowserRouter>
        {/* All routes will have persistent wallet connection */}
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/game" element={<GamePage />} />
          <Route path="/leaderboard" element={<LeaderboardPage />} />
        </Routes>
      </BrowserRouter>
    </IncentivWalletProvider>
  );
}

export default App;
```

---

## 📱 Basic Usage

### Wallet Connection Hook

```tsx
import { useIncentivWallet } from '@jim4565/dapp-wrapper-react';

function WalletInfo() {
  const { 
    address,           // Current wallet address
    isConnected,       // Connection status
    isConnecting,      // Loading state
    balance,           // Current balance in ETH
    isBalanceLoading,  // Balance loading state
    refreshBalance,    // Manual balance refresh
    connect,           // Connect function
    disconnect,        // Disconnect function
    error              // Any connection errors
  } = useIncentivWallet();

  if (!isConnected) {
    return (
      <button onClick={connect} disabled={isConnecting}>
        {isConnecting ? 'Connecting...' : 'Connect Wallet'}
      </button>
    );
  }

  return (
    <div>
      <p>Address: {address}</p>
      <p>Balance: {isBalanceLoading ? 'Loading...' : `${balance} ETH`}</p>
      <button onClick={disconnect}>Disconnect</button>
      <button onClick={refreshBalance}>Refresh Balance</button>
    </div>
  );
}
```

### Contract Interaction

**The beauty:** Your existing contract interaction code works exactly the same!

```tsx
import { useContract, useIncentivWallet } from '@jim4565/dapp-wrapper-react';
import { useState } from 'react';

function GameComponent() {
  const { isConnected } = useIncentivWallet();
  const { contract: gameContract, isLoading, error } = useContract("Game");
  const [isPlaying, setIsPlaying] = useState(false);

  const playGame = async () => {
    if (!gameContract || !isConnected) return;
    
    try {
      setIsPlaying(true);
      
      // Your exact @jim4565/dapp-wrapper syntax - no changes needed!
      await gameContract.playGame(true).withValue("0.1");
      
      alert('Game played successfully!');
    } catch (error) {
      console.error('Game failed:', error);
      alert('Game failed: ' + error.message);
    } finally {
      setIsPlaying(false);
    }
  };

  if (!isConnected) {
    return <p>Connect your wallet to play</p>;
  }

  if (isLoading) {
    return <p>Loading contract...</p>;
  }

  if (error) {
    return <p>Error loading contract: {error.message}</p>;
  }

  return (
    <button onClick={playGame} disabled={isPlaying}>
      {isPlaying ? 'Playing...' : 'Play Game (0.1 ETH)'}
    </button>
  );
}
```

### Ready-to-Use Components

```tsx
import { ConnectButton, WalletStatus } from '@jim4565/dapp-wrapper-react';

function Navigation() {
  return (
    <nav>
      {/* Ready-to-use wallet components */}
      <WalletStatus showBalance={true} />
      <ConnectButton />
    </nav>
  );
}
```

---

## ⚠️ Critical Navigation Rule

**ALWAYS use framework routing components to preserve wallet connection:**

```tsx
// ✅ CORRECT - Preserves wallet connection
import Link from 'next/link';               // Next.js
// import { Link } from 'react-router-dom';  // React Router

function Navigation() {
  return (
    <nav>
      <Link href="/game">Game</Link>           {/* ✅ Client-side routing */}
      <Link href="/leaderboard">Leaderboard</Link>  {/* ✅ Wallet stays connected */}
    </nav>
  );
}

// ❌ WRONG - Forces page reload and wallet reconnection
function BadNavigation() {
  return (
    <nav>
      <a href="/game">Game</a>                 {/* ❌ Page reload */}
      <a href="/leaderboard">Leaderboard</a>   {/* ❌ Wallet must reconnect */}
    </nav>
  );
}
```

---

## 🔧 API Reference

### Main Hooks

#### `useIncentivWallet()`
Main wallet hook providing connection state and actions.

```tsx
const {
  // Connection State
  address,           // string | undefined
  isConnected,       // boolean
  isConnecting,      // boolean
  error,            // Error | null
  
  // Balance State
  balance,           // string | undefined - current balance in ETH
  isBalanceLoading,  // boolean - balance loading state
  refreshBalance,    // () => Promise<void> - manual balance refresh
  
  // Actions
  connect,          // () => Promise<void>
  disconnect,       // () => void
  reconnect         // () => Promise<void>
} = useIncentivWallet();
```

#### `useContract(contractName)`
Access registered smart contracts.

```tsx
const { 
  contract,     // Contract instance with your methods
  isLoading,    // boolean
  error         // Error | null
} = useContract("GameContract");

// Use your contract exactly as before
await contract.playGame(true).withValue("0.1");
```


### Components

#### `<ConnectButton />`
Professional wallet connection button.

```tsx
<ConnectButton
  showAddress={true}
  showBalance={false}
  size="medium"
  variant="primary"
/>
```

#### `<WalletStatus />`
Comprehensive wallet status display.

```tsx
<WalletStatus
  showBalance={true}
  balanceDecimals={4}
  showFullAddress={false}
  size="medium"
/>
```

---

## 🏗️ How Persistence Works

### 3-Tier Reconnection Strategy

The library implements a professional reconnection strategy:

1. **Tier 1: Silent Check** - Check existing session without popups
2. **Tier 2: Session Restore** - Restore previous session silently  
3. **Tier 3: Full Connect** - Show connection popup only if user connected before

This ensures:
- ✅ **No unwanted popups** for first-time users
- ✅ **Seamless reconnection** for returning users
- ✅ **Fallback strategies** if silent methods fail

### What Gets Persisted

- ✅ **Wallet address** - Securely stored in localStorage
- ✅ **Connection status** - Whether user was connected
- ✅ **Ever connected flag** - Prevents popups for new users
- ✅ **Data expiration** - Auto-clears old data (30 days)

---

## 🔍 Troubleshooting

### Provider Not Found Error
```tsx
// ❌ Wrong: Component used outside provider
function App() {
  return (
    <div>
      <MyWalletComponent /> {/* Error: hook called outside provider */}
      <IncentivWalletProvider>
        <OtherComponents />
      </IncentivWalletProvider>
    </div>
  );
}

// ✅ Correct: All components inside provider
function App() {
  return (
    <IncentivWalletProvider>
      <div>
        <MyWalletComponent /> {/* Works: inside provider */}
        <OtherComponents />
      </div>
    </IncentivWalletProvider>
  );
}
```

### Wallet Disconnects When Navigating
```tsx
// ❌ Problem: Using <a> tags forces page reload
<a href="/game">Game</a>        // Page reload → wallet disconnect

// ✅ Solution: Use framework routing components
<Link href="/game">Game</Link>  // Client-side → wallet preserved
```

### Auto-Reconnect Not Working
```tsx
// Debug what's stored:
import { PersistenceService } from '@jim4565/dapp-wrapper-react';

const { address, wasConnected, hasEverConnected } = PersistenceService.loadWalletData();
console.log('Stored data:', { address, wasConnected, hasEverConnected });

// For auto-reconnect to work, you need:
// ✅ address: "0x..." (valid address)
// ✅ wasConnected: true (was connected last session)  
// ✅ hasEverConnected: true (user has connected before)
```

### Debug Mode
Enable logging to see what's happening:

```tsx
<IncentivWalletProvider 
  config={config}
  enableLogging={true}  // ✅ Enable debug logs
>
```

---

## 🎯 Migration from Raw @jim4565/dapp-wrapper

```tsx
// ❌ Before: Raw wrapper usage
import { IncentivWrapper } from '@jim4565/dapp-wrapper';

const wrapper = new IncentivWrapper();
await wrapper.connect();
wrapper.registerContract("Game", address, abi);
const contract = wrapper.getContract("Game");
await contract.playGame(true).withValue("0.1");
```

```tsx
// ✅ After: React wrapper (no changes to contract calls!)
import { IncentivWalletProvider, useContract } from '@jim4565/dapp-wrapper-react';

// 1. Wrap your app
<IncentivWalletProvider config={{ contracts: [...] }}>
  <App />
</IncentivWalletProvider>

// 2. Use hooks in components
function GameComponent() {
  const { contract } = useContract("Game");
  
  // Same syntax! No changes needed!
  await contract.playGame(true).withValue("0.1");
}
```

**Benefits of migrating:**
- ✅ Persistent connection across pages
- ✅ Automatic reconnection
- ✅ React hooks and components
- ✅ TypeScript improvements
- ✅ **Your contract code stays exactly the same!**

---

## 📄 License

MIT License - see [LICENSE](LICENSE) file for details.

---

**Built with ❤️ for the Incentiv ecosystem**

*Making Web3 development as easy as Web2*