---
title: "Headless Hook"
description: "useThunderPhone React ഹുക്ക് ഉപയോഗിച്ച് പൂർണമായും ഇഷ്ടാനുസൃത വോയ്‌സ് UI നിർമ്മിക്കുക"
---

`useThunderPhone` hook ഉപയോക്തൃ ഇന്റർഫേസിൽ നിങ്ങൾക്ക് പൂർണ നിയന്ത്രണം നൽകുന്നു, അതേസമയം ThunderPhone വോയ്‌സ് സെഷൻ, ഓഡിയോ റൂട്ടിംഗ്, കണക്ഷൻ സ്റ്റേറ്റ് എന്നിവ കൈകാര്യം ചെയ്യുന്നു. ThunderPhone പശ്ചാത്തലത്തിൽ എല്ലാം കൈകാര്യം ചെയ്യുമ്പോൾ, നിങ്ങളുടെ സ്വന്തം ബട്ടണുകൾ, ലേഔട്ടുകൾ, ആനിമേഷനുകൾ, ബ്രാൻഡിംഗ് എന്നിവയുള്ള പൂർണമായും കസ്റ്റം UI ആവശ്യമുള്ളപ്പോൾ ഇത് ഉപയോഗിക്കുക.

## ഹെഡ്‌ലെസ് Hook എപ്പോൾ ഉപയോഗിക്കണം

മുൻകൂട്ടി നിർമിച്ച `ThunderPhoneWidget` ഘടകം മിക്ക ഉപയോഗ സാഹചര്യങ്ങളും ഉൾക്കൊള്ളുന്നു, എന്നാൽ നിങ്ങൾക്ക് ഇനിപ്പറയുന്നവ ആവശ്യമുള്ളപ്പോൾ ഹെഡ്‌ലെസ് hook ഉപയോഗിക്കുക:

- നിങ്ങളുടെ ആപ്പിന്റെ ഡിസൈൻ സിസ്റ്റവുമായി പൊരുത്തപ്പെടുന്ന പൂർണമായും കസ്റ്റം കോൾ UI
- തത്സമയ ഓഡിയോ ലെവലുകൾ ഉപയോഗിച്ച് പ്രവർത്തിക്കുന്ന ഓഡിയോ-പ്രതികരണ ദൃശ്യവത്കരണങ്ങൾ (വേവ്‌ഫോമുകൾ, ഓർബുകൾ, പൾസിംഗ് ഇൻഡിക്കേറ്ററുകൾ)
- കോൾ-മുമ്പുള്ള ഫോമുകൾ, കോൾ-ശേഷമുള്ള സർവേകൾ, അല്ലെങ്കിൽ വോയ്‌സിനൊപ്പം ഇൻലൈൻ ചാറ്റ് പോലുള്ള കസ്റ്റം കോൾ ഫ്ലോകൾ
- നിലവിലുള്ള ഒരു ഘടക ലൈബ്രറിയിലേക്കുള്ള സംയോജനം (Material UI, Chakra, Radix മുതലായവ)

---

## ഇൻസ്റ്റലേഷൻ

```bash
npm install @thunderphone/widget
```

<Note>
  നിങ്ങൾ സ്വന്തമായി UI നൽകുന്നതിനാൽ ഹെഡ്‌ലെസ് hook-ന് `@thunderphone/widget/style.css` ഇംപോർട്ട് ചെയ്യേണ്ടതില്ല. എന്നിരുന്നാലും, അതേ `@thunderphone/widget` പാക്കേജ് നിങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്യണം.
</Note>

---

## അടിസ്ഥാന ഉപയോഗം

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function CustomCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  const handleClick = () => {
    if (phone.state === 'connected') {
      phone.disconnect()
    } else {
      phone.connect()
    }
  }

  return (
    <>
      <button onClick={handleClick} disabled={phone.state === 'connecting'}>
        {phone.state === 'connecting'
          ? 'Connecting...'
          : phone.state === 'connected'
            ? 'End call'
            : 'Start call'}
      </button>
      {phone.audio}
    </>
  )
}
```

<Warning>
  **നിങ്ങളുടെ ഘടക ട്രീയിൽ എവിടെയെങ്കിലും `phone.audio` റെൻഡർ ചെയ്യണം.** അടിസ്ഥാന ഓഡിയോ കണക്ഷൻ കൈകാര്യം ചെയ്യുന്ന അദൃശ്യ React എലമെന്റാണിത്. ഇത് ഒഴിവാക്കിയാൽ ഓഡിയോ പ്ലേ ചെയ്യില്ല, സെഷൻ പ്രവർത്തിക്കുകയുമില്ല.
</Warning>

---

## ഓപ്ഷനുകൾ

`UseThunderPhoneOptions` മുഖേന ഈ ഓപ്ഷനുകൾ `useThunderPhone`-ലേക്ക് നൽകുക:

| ഓപ്ഷൻ | തരം | ആവശ്യമാണ് | ഡിഫോൾട്ട് | വിവരണം |
|--------|------|----------|---------|-------------|
| `publishableKey` | `string` | അതെ | -- | പ്രസിദ്ധീകരിക്കാവുന്ന API കീ (`pk_live_...`). കീയുടെ വിജറ്റ് കോൺഫിഗറേഷനിൽ നിന്ന് ഏജന്റ് സ്വയമേവ നിർണയിക്കപ്പെടുന്നു. |
| `apiBase` | `string` | ഇല്ല | `'https://api.thunderphone.com/v1'` | API അടിസ്ഥാന URL ഓവർറൈഡ്. |
| `language` | `string` | ഇല്ല | -- | ഓരോ സെഷനിലുമുള്ള ഭാഷാ ഓവർറൈഡ് -- `en`, `es`, അല്ലെങ്കിൽ `fr-FR` പോലുള്ള ഭാഷാ കോഡോ ലോക്കേലോ. സജ്ജീകരിച്ചിട്ടില്ലെങ്കിൽ, ഏജന്റിന്റെ കോൺഫിഗർ ചെയ്ത ഭാഷ ബാധകമാകും. |
| `voice` | `string` | ഇല്ല | -- | ഓരോ സെഷനിലുമുള്ള വോയ്‌സ് ഓവർറൈഡ് -- `maria` പോലുള്ള വോയ്‌സ് നാമം. സജ്ജീകരിച്ചിട്ടില്ലെങ്കിൽ, ഏജന്റിന്റെ കോൺഫിഗർ ചെയ്ത വോയ്‌സ് ബാധകമാകും. |
| `context` | `string` | ഇല്ല | -- | ഏജന്റിലേക്ക് നൽകുന്ന ഓരോ സെഷനിലുമുള്ള വസ്തുതാപരമായ പേജ് അല്ലെങ്കിൽ സൈറ്റ് സന്ദർഭം. സെർവർ-സൈഡിൽ 12,000 പ്രതീകങ്ങളായി ചുരുക്കും. |
| `onConnect` | `() => void` | ഇല്ല | -- | വോയ്‌സ് സെഷൻ കണക്റ്റ് ചെയ്യുമ്പോൾ വിളിക്കപ്പെടുന്നു. |
| `onDisconnect` | `() => void` | ഇല്ല | -- | സെഷൻ അവസാനിക്കുമ്പോൾ വിളിക്കപ്പെടുന്നു. |
| `onError` | `(error) => void` | ഇല്ല | -- | പിശകുകൾ ഉണ്ടാകുമ്പോൾ വിളിക്കപ്പെടുന്നു. പിശകിന് `error` (കോഡ്), `message` ഫീൽഡുകളുണ്ട്. |
| `ringtone` | `boolean \| string` | ഇല്ല | `false` | കണക്റ്റ് ചെയ്യുമ്പോൾ റിങ്ടോൺ പ്ലേ ചെയ്യുക. ഡിഫോൾട്ട് റിങ്ടോണിന് `true`, അല്ലെങ്കിൽ കസ്റ്റം ഓഡിയോയ്ക്കായി URL സ്ട്രിംഗ് ഉപയോഗിക്കുക. |

<Note>
  ഈ hook ഹെഡ്‌ലെസ് ആണ്: ഇത് `ThunderPhoneWidget` രൂപഭാവ props (`theme`, `primaryColor`, `title`, `position`, `className`) സ്വീകരിക്കുന്നില്ല. അവ നൽകുന്നത് TypeScript പിശകാണ് -- അവതരണം നിർമിക്കേണ്ടത് പൂർണമായും നിങ്ങളുടേതാണ്.
</Note>

---

## മടക്ക മൂല്യം

ഹുക്ക് ഒരു `UseThunderPhoneReturn` ഒബ്ജക്റ്റ് മടക്കിനൽകുന്നു:

| പ്രോപ്പർട്ടി | തരം | വിവരണം |
|----------|------|-------------|
| `state` | `'idle' \| 'connecting' \| 'connected' \| 'disconnected' \| 'error'` | നിലവിലെ കണക്ഷൻ നില. |
| `connect` | `() => void` | ഒരു വോയ്‌സ് സെഷൻ ആരംഭിക്കുക. |
| `disconnect` | `() => void` | നിലവിലെ സെഷൻ അവസാനിപ്പിക്കുക. |
| `toggleMute` | `() => void` | മൈക്രോഫോൺ മ്യൂട്ട് ഓൺ/ഓഫ് ആക്കുക. |
| `isMuted` | `boolean` | മൈക്രോഫോൺ നിലവിൽ മ്യൂട്ട് ചെയ്തിട്ടുണ്ടോ എന്നത്. |
| `error` | `string \| undefined` | നില `'error'` ആയിരിക്കുമ്പോഴുള്ള പിശക് സന്ദേശം. |
| `agentName` | `string \| undefined` | കണക്റ്റ് ചെയ്ത ഏജന്റിന്റെ പ്രദർശന നാമം. |
| `audioLevel` | `number` | **ഒഴിവാക്കി -- എപ്പോഴും `0`.** ബാക്ക്‌വേഡ് കോംപാറ്റിബിലിറ്റിക്കായി നിലനിർത്തിയിരിക്കുന്ന ഒരു സ്റ്റാറ്റിക് പ്ലേസ്‌ഹോൾഡറാണിത്; ഇത് ഒരിക്കലും അപ്ഡേറ്റ് ചെയ്യില്ല. പകരം `audioLevelRef.current` വായിക്കുക. |
| `audioLevelRef` | `React.RefObject<number>` | ഏജന്റിന്റെ ശബ്ദത്തിന്റെയും സന്ദർശകന്റെ മൈക്രോഫോണിന്റെയും ശബ്ദത്തിൽ കൂടുതൽ ഉച്ചത്തിലുള്ളതായ, തത്സമയ ഓഡിയോ ലെവൽ (0--1) ഉൾക്കൊള്ളുന്ന ഒരു മാറ്റാവുന്ന ref -- React-ന്റെ റെൻഡർ സൈക്കിളിന് പുറത്തായി ഓരോ ആനിമേഷൻ ഫ്രെയിമിലും അപ്ഡേറ്റ് ചെയ്യുന്നു. സുഗമവും തടസ്സരഹിതവുമായ ആനിമേഷനുകൾക്കായി `requestAnimationFrame` ലൂപ്പുകൾക്കുള്ളിൽ `audioLevelRef.current` വായിക്കുക, അല്ലെങ്കിൽ React സ്റ്റേറ്റിൽ മൂല്യം ആവശ്യമുള്ളപ്പോൾ ഇടവേളകളിൽ അത് സാമ്പിൾ ചെയ്യുക. |
| `audio` | `ReactNode` | ഓഡിയോ കണക്ഷൻ കൈകാര്യം ചെയ്യുന്ന അദൃശ്യ എലമെന്റ് -- **റെൻഡർ ചെയ്യണം**. |

---

## ഓഡിയോ-പ്രതികരണ UI

`audioLevelRef` ref React റീ-റെൻഡറുകൾ ട്രിഗർ ചെയ്യാതെ ഫ്രെയിം-റേറ്റ് ഓഡിയോ ലെവലുകൾ നൽകുന്നു. അതിനാൽ സുഗമമായ വേവ്‌ഫോം വിഷ്വലൈസേഷനുകൾ, പൾസിംഗ് ഓർബുകൾ, അല്ലെങ്കിൽ സംഭാഷണവുമായി ബന്ധിപ്പിച്ചിട്ടുള്ള ഏതെങ്കിലും ആനിമേഷൻ എന്നിവ നിയന്ത്രിക്കാൻ ഇത് അനുയോജ്യമാണ്. ഏജന്റിന്റെ ശബ്ദമോ സന്ദർശകന്റെ മൈക്രോഫോണോ—ഏതാണ് ഉച്ചത്തിലുള്ളതെന്ന് ലെവൽ പ്രതിഫലിപ്പിക്കുന്നു.

### വേവ്‌ഫോം ഉദാഹരണം

```tsx
import { useRef, useEffect } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function WaveformCall() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    if (phone.state !== 'connected') return
    const canvas = canvasRef.current
    if (!canvas) return
    const ctx = canvas.getContext('2d')!

    let animId: number
    const draw = () => {
      const level = phone.audioLevelRef.current ?? 0
      ctx.clearRect(0, 0, canvas.width, canvas.height)

      // Draw bars that react to audio level
      const barCount = 24
      const barWidth = canvas.width / barCount
      for (let i = 0; i < barCount; i++) {
        const distance = Math.abs(i - barCount / 2) / (barCount / 2)
        const height = level * canvas.height * (1 - distance * 0.6)
        const y = (canvas.height - height) / 2
        ctx.fillStyle = '#0ea5e9'
        ctx.fillRect(i * barWidth + 1, y, barWidth - 2, height)
      }

      animId = requestAnimationFrame(draw)
    }
    animId = requestAnimationFrame(draw)
    return () => cancelAnimationFrame(animId)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div>
      {phone.state === 'connected' && (
        <canvas ref={canvasRef} width={240} height={80} />
      )}
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'End call' : 'Start call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

### പൾസിംഗ് ഓർബ് ഉദാഹരണം

```tsx
import { useRef, useEffect } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function PulsingOrb() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const orbRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (phone.state !== 'connected') return
    let animId: number
    const animate = () => {
      const level = phone.audioLevelRef.current ?? 0
      if (orbRef.current) {
        const scale = 1 + level * 0.5
        orbRef.current.style.transform = `scale(${scale})`
        orbRef.current.style.opacity = `${0.6 + level * 0.4}`
      }
      animId = requestAnimationFrame(animate)
    }
    animId = requestAnimationFrame(animate)
    return () => cancelAnimationFrame(animId)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div style={{ textAlign: 'center' }}>
      <div
        ref={orbRef}
        style={{
          width: 80,
          height: 80,
          borderRadius: '50%',
          background: '#0ea5e9',
          margin: '20px auto',
          transition: 'transform 0.05s ease-out',
        }}
      />
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'End call' : 'Call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

### സംസാര സൂചക ഉദാഹരണം

വോളിയം അനുസരിച്ച് മാറുന്ന React-റെൻഡർ ചെയ്ത UI-യ്ക്കായി -- ത്രെഷോൾഡ് അടിസ്ഥാനമാക്കിയുള്ള "സംസാരിക്കുന്നു" ബാഡ്ജ് പോലെ -- `audioLevelRef.current` ഒരു ഇടവേളയിൽ സാമ്പിൾ ചെയ്ത് ഫലം സ്റ്റേറ്റിൽ സൂക്ഷിക്കുക:

```tsx
import { useEffect, useState } from 'react'
import { useThunderPhone } from '@thunderphone/widget'

function SpeakingBadge() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })
  const [speaking, setSpeaking] = useState(false)

  useEffect(() => {
    if (phone.state !== 'connected') {
      setSpeaking(false)
      return
    }
    const interval = setInterval(() => {
      setSpeaking((phone.audioLevelRef.current ?? 0) > 0.1)
    }, 100)
    return () => clearInterval(interval)
  }, [phone.state, phone.audioLevelRef])

  return (
    <div>
      {phone.state === 'connected' && (
        <span>{speaking ? 'Speaking' : 'Listening'}</span>
      )}
      <button onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}>
        {phone.state === 'connected' ? 'End call' : 'Start call'}
      </button>
      {phone.audio}
    </div>
  )
}
```

<Warning>
  എല്ലായ്പ്പോഴും `audioLevelRef.current`-ൽ നിന്ന് ലെവലുകൾ വായിക്കുക. റിട്ടേൺ ഒബ്‌ജക്റ്റിലെ `audioLevel` നമ്പർ **ഒഴിവാക്കിയിരിക്കുന്നു, എപ്പോഴും `0` ആയിരിക്കും** -- അതിനെ അടിസ്ഥാനമാക്കിയുള്ള ഏത് ലോജിക്കും അറിയിപ്പില്ലാതെ പൂജ്യം വായിക്കും.
</Warning>

---

## സ്റ്റേറ്റ് മെഷീൻ

`state` പ്രോപ്പർട്ടി ഈ ലൈഫ്‌സൈക്കിൾ പിന്തുടരുന്നു:

```
idle --> connecting --> connected --> disconnected --> idle (after 1.5s)
                  \
                   --> error (stays until connect() is called again)
```

| സ്റ്റേറ്റ് | വിവരണം |
|-------|-------------|
| `idle` | സജീവ സെഷൻ ഇല്ല. `connect()` വിളിക്കാൻ തയ്യാറാണ്. |
| `connecting` | സെഷൻ സ്ഥാപിച്ചുകൊണ്ടിരിക്കുന്നു. ഈ സ്റ്റേറ്റിൽ കോൾ ബട്ടൺ പ്രവർത്തനരഹിതമാക്കുക. |
| `connected` | വോയ്‌സ് സെഷൻ സജീവമാണ്. ഉപയോക്താവ് ഏജന്റുമായി സംസാരിക്കുകയാണ്. |
| `disconnected` | സെഷൻ ക്രമമായി അവസാനിച്ചു. 1.5 സെക്കൻഡിന് ശേഷം സ്വയമേവ `idle`-ലേക്ക് മടങ്ങും. |
| `error` | എന്തോ പിശക് സംഭവിച്ചു. സന്ദേശത്തിനായി `phone.error` പരിശോധിക്കുക. സ്റ്റേറ്റ് സ്വയം മായുന്നില്ല -- `connect()` വീണ്ടും വിളിക്കുന്നത് പുതിയ ശ്രമം ആരംഭിക്കുകയും പിശക് റീസെറ്റ് ചെയ്യുകയും ചെയ്യും. |

---

## ഉദാഹരണങ്ങൾ

### മ്യൂട്ട് നിയന്ത്രണത്തോടൊപ്പം

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function CallWithMute() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  return (
    <div>
      {phone.state === 'connected' && (
        <div>
          <p>Talking to {phone.agentName ?? 'Agent'}</p>
          <button onClick={phone.toggleMute}>
            {phone.isMuted ? 'Unmute' : 'Mute'}
          </button>
          <button onClick={phone.disconnect}>End call</button>
        </div>
      )}

      {phone.state !== 'connected' && (
        <button
          onClick={phone.connect}
          disabled={phone.state === 'connecting'}
        >
          {phone.state === 'connecting' ? 'Connecting...' : 'Call support'}
        </button>
      )}

      {phone.state === 'error' && (
        <p style={{ color: 'red' }}>{phone.error}</p>
      )}

      {phone.audio}
    </div>
  )
}
```

### റിങ്ടോണിനൊപ്പം

ഫോൺ കോൾ അനുകരിക്കാൻ കണക്റ്റ് ചെയ്യുന്നതിനിടെ റിങ് ചെയ്യുന്ന ശബ്ദം പ്ലേ ചെയ്യുക:

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function PhoneCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
    ringtone: true, // or a custom URL: 'https://example.com/ringtone.mp3'
  })

  return (
    <>
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connecting'
          ? 'Ringing...'
          : phone.state === 'connected'
            ? 'Hang up'
            : 'Call'}
      </button>
      {phone.audio}
    </>
  )
}
```

`connecting` സ്റ്റേറ്റിൽ റിങ്ടോൺ ആവർത്തിച്ച് പ്ലേ ചെയ്യുകയും ഏജന്റ് കണക്റ്റ് ചെയ്യുമ്പോൾ ക്രമേണ മങ്ങിപ്പോകുകയും ചെയ്യുന്നു. ബിൽറ്റ്-ഇൻ ഡിഫോൾട്ട് റിങ്ടോണിനായി `true` നൽകുക, അല്ലെങ്കിൽ നിങ്ങളുടെ സ്വന്തം ഓഡിയോ ഫയൽ ഉപയോഗിക്കാൻ ഒരു URL സ്ട്രിംഗ് നൽകുക.

### ഇവന്റ് കോൾബാക്കുകളോടൊപ്പം

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function TrackedCallButton() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
    onConnect: () => {
      analytics.track('call_started')
    },
    onDisconnect: () => {
      analytics.track('call_ended')
    },
    onError: (error) => {
      analytics.track('call_error', { code: error.error, message: error.message })
    },
  })

  return (
    <>
      <button
        onClick={phone.state === 'connected' ? phone.disconnect : phone.connect}
        disabled={phone.state === 'connecting'}
      >
        {phone.state === 'connected' ? 'Hang up' : 'Talk to AI'}
      </button>
      {phone.audio}
    </>
  )
}
```

### പൂർണ്ണമായ കസ്റ്റം UI

```tsx
import { useThunderPhone } from '@thunderphone/widget'

function FullCustomUI() {
  const phone = useThunderPhone({
    publishableKey: 'pk_live_your_publishable_key',
  })

  return (
    <div className="call-panel">
      <div className="call-status">
        {phone.state === 'idle' && <span>Ready</span>}
        {phone.state === 'connecting' && <span className="pulse">Connecting...</span>}
        {phone.state === 'connected' && (
          <span>On call with {phone.agentName}</span>
        )}
        {phone.state === 'error' && <span className="error">{phone.error}</span>}
      </div>

      <div className="call-controls">
        {phone.state === 'connected' ? (
          <>
            <button className="mute-btn" onClick={phone.toggleMute}>
              {phone.isMuted ? 'Unmute' : 'Mute'}
            </button>
            <button className="end-btn" onClick={phone.disconnect}>
              End
            </button>
          </>
        ) : (
          <button
            className="start-btn"
            onClick={phone.connect}
            disabled={phone.state === 'connecting'}
          >
            Start call
          </button>
        )}
      </div>

      {/* Required -- handles audio under the hood */}
      {phone.audio}
    </div>
  )
}
```

---

## നിർദേശങ്ങൾ

<AccordionGroup>
  <Accordion title="എല്ലായ്പ്പോഴും phone.audio റെൻഡർ ചെയ്യുക">
    `phone.audio` എലമെന്റ് ദൃശ്യമല്ലെങ്കിലും ആവശ്യമാണ്. ഇത് നിങ്ങളുടെ JSX-ൽ എവിടെയും സ്ഥാപിക്കുക -- ഇത് ദൃശ്യമാകുന്ന DOM ഒന്നും റെൻഡർ ചെയ്യില്ല, പക്ഷേ WebRTC ഓഡിയോ കണക്ഷൻ ആന്തരികമായി കൈകാര്യം ചെയ്യുന്നു.
  </Accordion>

  <Accordion title="കണക്റ്റ് ചെയ്യുമ്പോൾ ബട്ടൺ പ്രവർത്തനരഹിതമാക്കുക">
    `connecting` സ്റ്റേറ്റ് 1-3 സെക്കൻഡ് നീണ്ടുനിൽക്കാം. ഡ്യൂപ്ലിക്കേറ്റ് കണക്ഷൻ ശ്രമങ്ങൾ തടയാൻ ഈ സ്റ്റേറ്റിൽ കോൾ ബട്ടൺ പ്രവർത്തനരഹിതമാക്കുക.
  </Accordion>

  <Accordion title="എറർ സ്റ്റേറ്റ് സുഗമമായി കൈകാര്യം ചെയ്യുക">
    സ്റ്റേറ്റ് `error` ആയിരിക്കുമ്പോൾ, ഉപയോക്താവിന് `phone.error` പ്രദർശിപ്പിക്കുകയും നിങ്ങളുടെ കോൾ ബട്ടൺ പ്രവർത്തനക്ഷമമായി നിലനിർത്തുകയും ചെയ്യുക. ഹുക്ക് സ്വയം `error` സ്റ്റേറ്റ് വിടുന്നില്ല -- `connect()` വീണ്ടും വിളിക്കുന്നത് പുതിയ ശ്രമം ആരംഭിക്കുകയും മുമ്പത്തെ എറർ മായ്ക്കുകയും ചെയ്യുന്നു.
  </Accordion>

  <Accordion title="സൈഡ് ഇഫക്റ്റുകൾക്കായി കോൾബാക്കുകൾ ഉപയോഗിക്കുക">
    സ്റ്റേറ്റ് പോൾ ചെയ്യാതെ അനലിറ്റിക്സ്, ലോഗിംഗ്, അല്ലെങ്കിൽ മറ്റ് ആപ്ലിക്കേഷൻ ലോജിക് ട്രിഗർ ചെയ്യുന്നതിനായി `onConnect`, `onDisconnect`, `onError` കോൾബാക്കുകൾ അനുയോജ്യമാണ്.
  </Accordion>

  <Accordion title="audioLevelRef-ൽ നിന്ന് ഓഡിയോ ലെവലുകൾ വായിക്കുക">
    `audioLevelRef` മാത്രമാണ് ലൈവ് ഓഡിയോ-ലെവൽ സോഴ്സ്. വേവ്ഫോം പോലുള്ള സുഗമമായ ആനിമേഷനുകൾക്കായി `requestAnimationFrame`-നുള്ളിൽ `audioLevelRef.current` വായിക്കുക (ഒരു ref വായിക്കുന്നത് റീ-റെൻഡറുകൾക്ക് കാരണമാകില്ല), അല്ലെങ്കിൽ ഒരു ഇടവേളയിൽ അത് സാമ്പിൾ ചെയ്ത് React-റെൻഡർ ചെയ്ത UI-നായി ഫലം സ്റ്റേറ്റിൽ സംഭരിക്കുക. `audioLevel` നമ്പർ ഡിപ്രിക്കേറ്റഡ് ആണ്, എപ്പോഴും `0` ആയിരിക്കും -- അതിനെ അടിസ്ഥാനമാക്കി ലോജിക് നിർമിക്കരുത്.
  </Accordion>
</AccordionGroup>
