useWayfinder

Overview

The useWayfinder hook provides access to the complete Wayfinder instance from the React context. This hook gives you full control over all Wayfinder methods and is ideal for advanced usage scenarios where you need access to multiple Wayfinder capabilities.

Signature

function useWayfinder(): WayfinderContextValue

interface WayfinderContextValue {
  wayfinder: Wayfinder
}

Usage

Basic Usage

import { useWayfinder } from '@ar.io/wayfinder-react'

function MyComponent() {
  const { wayfinder } = useWayfinder()

  const handleFetch = async () => {
    try {
      const response = await wayfinder.request('ar://transaction-id')
      const data = await response.text()
      console.log(data)
    } catch (error) {
      console.error('Failed to fetch:', error)
    }
  }

  const handleResolve = async () => {
    try {
      const url = await wayfinder.resolveUrl({
        originalUrl: 'ar://transaction-id',
      })
      console.log('Resolved URL:', url.toString())
    } catch (error) {
      console.error('Failed to resolve:', error)
    }
  }

  return (
    <div>
      <button onClick={handleFetch}>Fetch Data</button>
      <button onClick={handleResolve}>Resolve URL</button>
    </div>
  )
}

Advanced Usage with Event Listeners

import { useWayfinder } from '@ar.io/wayfinder-react'
import { useEffect, useState } from 'react'

function AdvancedComponent() {
  const { wayfinder } = useWayfinder()
  const [events, setEvents] = useState([])

  useEffect(() => {
    // Listen to routing events
    const handleRoutingSuccess = (event) => {
      setEvents((prev) => [
        ...prev,
        {
          type: 'routing-success',
          data: event,
          timestamp: Date.now(),
        },
      ])
    }

    const handleRoutingFailed = (error) => {
      setEvents((prev) => [
        ...prev,
        {
          type: 'routing-failed',
          data: error,
          timestamp: Date.now(),
        },
      ])
    }

    const handleVerificationSuccess = (event) => {
      setEvents((prev) => [
        ...prev,
        {
          type: 'verification-success',
          data: event,
          timestamp: Date.now(),
        },
      ])
    }

    // Subscribe to events
    wayfinder.emitter.on('routing-succeeded', handleRoutingSuccess)
    wayfinder.emitter.on('routing-failed', handleRoutingFailed)
    wayfinder.emitter.on('verification-succeeded', handleVerificationSuccess)

    // Cleanup
    return () => {
      wayfinder.emitter.off('routing-succeeded', handleRoutingSuccess)
      wayfinder.emitter.off('routing-failed', handleRoutingFailed)
      wayfinder.emitter.off('verification-succeeded', handleVerificationSuccess)
    }
  }, [wayfinder])

  return (
    <div>
      <h3>Wayfinder Events</h3>
      <ul>
        {events.map((event, index) => (
          <li key={index}>
            {event.type} - {new Date(event.timestamp).toLocaleTimeString()}
          </li>
        ))}
      </ul>
    </div>
  )
}

Custom Request with Overrides

import { useWayfinder } from '@ar.io/wayfinder-react'
import { useState } from 'react'
import {
  StaticRoutingStrategy,
  HashVerificationStrategy,
} from '@ar.io/wayfinder-core'

function CustomRequestComponent({ txId }) {
  const { wayfinder } = useWayfinder()
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)

  const fetchWithCustomSettings = async () => {
    setLoading(true)
    setError(null)

    try {
      // Override routing and verification for this specific request
      const response = await wayfinder.request(`ar://${txId}`, {
        routingSettings: {
          strategy: new StaticRoutingStrategy({
            gateway: 'https://arweave.net',
          }),
        },
        verificationSettings: {
          enabled: true,
          strict: true,
          strategy: new HashVerificationStrategy({
            trustedGateways: ['https://arweave.net'],
          }),
        },
      })

      const text = await response.text()
      setData(text)
    } catch (err) {
      setError(err)
    } finally {
      setLoading(false)
    }
  }

  return (
    <div>
      <button onClick={fetchWithCustomSettings} disabled={loading}>
        {loading
          ? 'Fetching with custom settings...'
          : 'Fetch with Custom Settings'}
      </button>

      {error && <div className="error">Error: {error.message}</div>}
      {data && <pre>{data}</pre>}
    </div>
  )
}

Error Handling

Provider Context Error

The hook throws an error if used outside of a WayfinderProvider:

function ComponentOutsideProvider() {
  try {
    const { wayfinder } = useWayfinder()
    // This will never execute
  } catch (error) {
    // Error: useWayfinder must be used within a WayfinderProvider
    console.error(error.message)
  }

  return <div>Component outside provider</div>
}

Proper Error Handling

import { useWayfinder } from '@ar.io/wayfinder-react'
import { useState, useCallback } from 'react'

function RobustComponent() {
  const { wayfinder } = useWayfinder()
  const [error, setError] = useState(null)
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(false)

  const handleRequest = useCallback(
    async (txId) => {
      setLoading(true)
      setError(null)

      try {
        const response = await wayfinder.request(`ar://${txId}`)
        const result = await response.text()
        setData(result)
      } catch (err) {
        setError(err)

        // Log different error types
        if (err.name === 'TimeoutError') {
          console.error('Request timed out')
        } else if (err.name === 'VerificationError') {
          console.error('Data verification failed')
        } else if (err.name === 'NetworkError') {
          console.error('Network error occurred')
        } else {
          console.error('Unknown error:', err)
        }
      } finally {
        setLoading(false)
      }
    },
    [wayfinder],
  )

  const clearError = useCallback(() => {
    setError(null)
  }, [])

  return (
    <div>
      {error && (
        <div className="error-banner">
          <p>Error: {error.message}</p>
          <button onClick={clearError}>Dismiss</button>
          <button onClick={() => handleRequest('retry')}>Retry</button>
        </div>
      )}

      {loading && <div>Loading...</div>}
      {data && <pre>{data}</pre>}
    </div>
  )
}

Performance Considerations

Memoization

The Wayfinder instance is automatically memoized in the provider, but you should memoize callbacks that use it:

import { useWayfinder } from '@ar.io/wayfinder-react'
import { useCallback, useMemo } from 'react'

function OptimizedComponent({ txIds }) {
  const { wayfinder } = useWayfinder()

  // Memoize the request function
  const fetchData = useCallback(
    async (txId) => {
      const response = await wayfinder.request(`ar://${txId}`)
      return response.text()
    },
    [wayfinder],
  )

  // Memoize derived data
  const processedTxIds = useMemo(() => {
    return txIds.filter((id) => id.length === 43) // Valid Arweave TX IDs
  }, [txIds])

  return (
    <div>
      {processedTxIds.map((txId) => (
        <DataItem key={txId} txId={txId} fetchData={fetchData} />
      ))}
    </div>
  )
}

Event Listener Cleanup

Always clean up event listeners to prevent memory leaks:

import { useWayfinder } from '@ar.io/wayfinder-react'
import { useEffect, useRef } from 'react'

function EventListenerComponent() {
  const { wayfinder } = useWayfinder()
  const handlersRef = useRef({})

  useEffect(() => {
    // Store handlers in ref for cleanup
    handlersRef.current.onRoutingSuccess = (event) => {
      console.log('Routing succeeded:', event)
    }

    handlersRef.current.onRoutingFailed = (error) => {
      console.error('Routing failed:', error)
    }

    // Add listeners
    wayfinder.emitter.on(
      'routing-succeeded',
      handlersRef.current.onRoutingSuccess,
    )
    wayfinder.emitter.on('routing-failed', handlersRef.current.onRoutingFailed)

    // Cleanup function
    return () => {
      wayfinder.emitter.off(
        'routing-succeeded',
        handlersRef.current.onRoutingSuccess,
      )
      wayfinder.emitter.off(
        'routing-failed',
        handlersRef.current.onRoutingFailed,
      )
    }
  }, [wayfinder])

  return <div>Component with event listeners</div>
}

TypeScript Support

Typed Usage

import { useWayfinder } from '@ar.io/wayfinder-react'
import { WayfinderContextValue } from '@ar.io/wayfinder-react'
import { Wayfinder, WayfinderEvent } from '@ar.io/wayfinder-core'

interface ComponentProps {
  txId: string
  onSuccess?: (data: string) => void
  onError?: (error: Error) => void
}

const TypedComponent: React.FC<ComponentProps> = ({
  txId,
  onSuccess,
  onError,
}) => {
  const context: WayfinderContextValue = useWayfinder()
  const wayfinder: Wayfinder = context.wayfinder

  const handleFetch = async (): Promise<void> => {
    try {
      const response = await wayfinder.request(`ar://${txId}`)
      const data = await response.text()
      onSuccess?.(data)
    } catch (error) {
      onError?.(error as Error)
    }
  }

  // Type-safe event handling
  const handleRoutingEvent = (
    event: WayfinderEvent['routing-succeeded'],
  ): void => {
    console.log('Selected gateway:', event.selectedGateway)
  }

  return <button onClick={handleFetch}>Fetch Data</button>
}

Custom Hook with TypeScript

import { useWayfinder } from '@ar.io/wayfinder-react'
import { useState, useCallback } from 'react'

interface UseWayfinderDataResult {
  data: string | null
  loading: boolean
  error: Error | null
  fetchData: (txId: string) => Promise<void>
  clearData: () => void
}

function useWayfinderData(): UseWayfinderDataResult {
  const { wayfinder } = useWayfinder()
  const [data, setData] = useState<string | null>(null)
  const [loading, setLoading] = useState<boolean>(false)
  const [error, setError] = useState<Error | null>(null)

  const fetchData = useCallback(
    async (txId: string): Promise<void> => {
      setLoading(true)
      setError(null)

      try {
        const response = await wayfinder.request(`ar://${txId}`)
        const result = await response.text()
        setData(result)
      } catch (err) {
        setError(err as Error)
      } finally {
        setLoading(false)
      }
    },
    [wayfinder],
  )

  const clearData = useCallback((): void => {
    setData(null)
    setError(null)
  }, [])

  return {
    data,
    loading,
    error,
    fetchData,
    clearData,
  }
}

// Usage
function MyComponent() {
  const { data, loading, error, fetchData, clearData } = useWayfinderData()

  return (
    <div>
      <button onClick={() => fetchData('transaction-id')}>Fetch</button>
      <button onClick={clearData}>Clear</button>
      {loading && <div>Loading...</div>}
      {error && <div>Error: {error.message}</div>}
      {data && <pre>{data}</pre>}
    </div>
  )
}

Testing

Mocking the Hook

import { render, screen } from '@testing-library/react'
import { useWayfinder } from '@ar.io/wayfinder-react'

// Mock the hook
jest.mock('@ar.io/wayfinder-react', () => ({
  useWayfinder: jest.fn(),
}))

const mockUseWayfinder = useWayfinder as jest.MockedFunction<
  typeof useWayfinder
>

test('component using useWayfinder', () => {
  const mockWayfinder = {
    request: jest.fn().mockResolvedValue({
      text: () => Promise.resolve('test data'),
    }),
    resolveUrl: jest.fn(),
    emitter: {
      on: jest.fn(),
      off: jest.fn(),
    },
  }

  mockUseWayfinder.mockReturnValue({ wayfinder: mockWayfinder })

  render(<MyComponent />)

  // Test component behavior
  expect(screen.getByText('Fetch Data')).toBeInTheDocument()
})

When to Use

Use useWayfinder when you need:

  • Full Wayfinder API access: Access to all methods like request(), resolveUrl(), and event emitters
  • Event monitoring: Listening to routing, verification, or other Wayfinder events
  • Custom request configurations: Overriding routing or verification settings per request
  • Advanced integrations: Building complex components that need multiple Wayfinder capabilities
  • Custom abstractions: Creating your own hooks or utilities that wrap Wayfinder functionality

For simpler use cases, consider:

Was this page helpful?