# Hooks API

本節提供該函式庫中可用的自訂 React Hooks 的詳細參考。這些 Hooks 旨在封裝和重用有狀態的邏輯，簡化 Blocklet 環境中的常見任務和互動。

## useComponentInstalled

此 Hook 檢查一個或多個指定的選擇性元件（透過其 DID 識別）是否已安裝。對於實作依賴其他 Blocklet 作為相依項的功能至關重要。此 Hook 提供安裝狀態和必要的 URL，以便在需要時提示使用者進行安裝。

### 參數

此 Hook 接受一個包含以下屬性的物件：

<x-field-group>
  <x-field data-name="did" data-type="string | string[]" data-required="true">
    <x-field-desc markdown>要檢查的元件的去中心化識別碼（DID）或 DID 陣列。單一字串可以包含多個以 `;;` 分隔的 DID。</x-field-desc>
  </x-field>
  <x-field data-name="onInstalled" data-type="function" data-required="false">
    <x-field-desc markdown>一個選擇性的回呼函式，當所有指定的元件都已安裝時觸發。</x-field-desc>
  </x-field>
  <x-field data-name="onError" data-type="function" data-required="false">
    <x-field-desc markdown>一個選擇性的回呼函式，當一個或多個指定的元件未安裝時觸發。</x-field-desc>
  </x-field>
</x-field-group>

### 回傳值

它回傳一個包含安裝狀態和相關資料的物件：

<x-field-group>
  <x-field data-name="optComponents" data-type="array">
    <x-field-desc markdown>一個未安裝的元件物件陣列。每個物件包含元資料，如 `meta.did`、`storeUrl` 和 `installUrl`。</x-field-desc>
  </x-field>
  <x-field data-name="installed" data-type="boolean">
    <x-field-desc markdown>一個布林值，如果所有指定的元件都已安裝且在 `blocklet.yml` 中定義，則為 `true`，否則為 `false`。</x-field-desc>
  </x-field>
  <x-field data-name="installStatus" data-type="object">
    <x-field-desc markdown>一個物件，其中鍵是元件的 DID，值是其目前的安裝狀態（例如「waiting」、「installing」）。此狀態透過 `window.postMessage` 事件更新。</x-field-desc>
  </x-field>
  <x-field data-name="setInstallStatus" data-type="function">
    <x-field-desc markdown>用於手動更新 `installStatus` 物件的狀態設定函式。</x-field-desc>
  </x-field>
  <x-field data-name="definedInBlockletYML" data-type="boolean">
    <x-field-desc markdown>一個布林值，表示該元件是否在 blocklet 的設定檔（`blocklet.yml`）中定義。</x-field-desc>
  </x-field>
</x-field-group>

### 使用範例

以下範例示範如何使用 `useComponentInstalled` 來條件性地渲染一個功能，或在缺少相依項時渲染一個 `ComponentInstaller` 元件。

```javascript "ComponentFeature.js" icon=logos:javascript
import React from 'react';
import { useComponentInstalled, ComponentInstaller } from '@blocklet/ui-react';

const REQUIRED_DID = 'z8ia24z55nve2TSF5m1aZ5322d9f48a43D4a'; // 範例 DID

function ComponentFeature() {
  const { installed, optComponents } = useComponentInstalled({ did: REQUIRED_DID });

  if (!installed) {
    // 如果元件不存在，則渲染安裝程式 UI
    return <ComponentInstaller components={optComponents} />;
  }

  // 渲染依賴於已安裝元件的功能
  return (
    <div>
      <h2>My Feature</h2>
      <p>This feature requires the component with DID: {REQUIRED_DID}</p>
      {/* ... 功能實作 ... */}
    </div>
  );
}

export default ComponentFeature;
```

## useFollow

此 Hook 管理目前已驗證的使用者與另一位由其 DID 指定的使用者之間的追蹤關係。它處理追蹤和取消追蹤的 API 呼叫，並提供目前的追蹤狀態。

### 參數

<x-field-group>
  <x-field data-name="userDid" data-type="string" data-required="true">
    <x-field-desc markdown>要檢查追蹤狀態的使用者設定檔的 DID。</x-field-desc>
  </x-field>
  <x-field data-name="t" data-type="function" data-required="true">
    <x-field-desc markdown>用於顯示成功或錯誤訊息的翻譯函式（例如，來自 `react-i18next`）。</x-field-desc>
  </x-field>
  <x-field data-name="isMySelf" data-type="boolean" data-required="true">
    <x-field-desc markdown>如果 `userDid` 屬於目前登入的使用者，則應設定為 `true` 的布林值。</x-field-desc>
  </x-field>
</x-field-group>

### 回傳值

回傳一個包含以下屬性的物件：

<x-field-group>
  <x-field data-name="followed" data-type="boolean">
    <x-field-desc markdown>表示目前使用者是否正在追蹤由 `userDid` 指定的使用者。如果正在追蹤則為 `true`，否則為 `false`。</x-field-desc>
  </x-field>
  <x-field data-name="followUser" data-type="function">
    <x-field-desc markdown>一個穩定的函式，用於呼叫以追蹤使用者。它處理 API 請求並在成功時更新 `followed` 狀態。</x-field-desc>
  </x-field>
  <x-field data-name="unfollowUser" data-type="function">
    <x-field-desc markdown>一個穩定的函式，用於呼叫以取消追蹤使用者。它處理 API 請求並在成功時更新 `followed` 狀態。</x-field-desc>
  </x-field>
</x-field-group>

### 使用範例

此範例展示如何建立一個 `FollowButton` 元件，根據關係狀態顯示「追蹤」或「取消追蹤」操作。

```javascript "FollowButton.js" icon=logos:javascript
import React from 'react';
import Button from '@mui/material/Button';
import { useTranslation } from 'react-i18next';
import { useFollow } from '@blocklet/ui-react/hooks';
import { useSession } from '@blocklet/did-connect-react';

function FollowButton({ profileDid }) {
  const { session } = useSession();
  const { t } = useTranslation();
  const isMySelf = session?.user?.did === profileDid;

  const { followed, followUser, unfollowUser } = useFollow({
    userDid: profileDid,
    t,
    isMySelf,
  });

  if (isMySelf) {
    return null; // 不在自己的個人資料頁面上顯示按鈕
  }

  const handleClick = () => {
    if (followed) {
      unfollowUser();
    } else {
      followUser();
    }
  };

  return (
    <Button variant="contained" onClick={handleClick}>
      {followed ? t('profile.unfollow') : t('profile.follow')}
    </Button>
  );
}

export default FollowButton;
```

## useMobile

一個用於建立響應式元件的簡單工具 Hook。它利用 Material-UI 的 `useMediaQuery` 來判斷目前的視窗寬度是否低於指定的斷點。

### 參數

<x-field-group>
  <x-field data-name="key" data-type="number | Breakpoint" data-default="'sm'" data-required="false">
    <x-field-desc markdown>Material-UI 的斷點鍵（例如 `'xs'`、`'sm'`、`'md'`）或一個像素值用於檢查。如果螢幕寬度小於此值，此 Hook 回傳 `true`。</x-field-desc>
  </x-field>
</x-field-group>

### 回傳值

回傳一個 `boolean` 值：如果視窗小於指定的斷點，則為 `true`，否則為 `false`。

### 使用範例

此 Hook 必須在被 Material-UI `ThemeProvider` 包裹的元件樹中使用。

```javascript "ResponsiveComponent.js" icon=logos:javascript
import React from 'react';
import Typography from '@mui/material/Typography';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { useMobile } from '@blocklet/ui-react/hooks';

// 一個使用此 Hook 的元件
function ResponsiveComponent() {
  const isMobile = useMobile({ key: 'md' }); // 根據 'md' 斷點進行檢查

  return (
    <div>
      {isMobile ? (
        <Typography variant="h6">Mobile View</Typography>
      ) : (
        <Typography variant="h4">Desktop View</Typography>
      )}
      <p>Resize your browser window to see the content change.</p>
    </div>
  );
}

// 該元件必須被 ThemeProvider 包裹
const theme = createTheme();

function App() {
  return (
    <ThemeProvider theme={theme}>
      <ResponsiveComponent />
    </ThemeProvider>
  );
}

export default App;
```

---

透過使用這些 Hooks，您可以用最少的樣板程式碼高效地實作複雜的功能。有關可能使用這些 Hooks 的元件的更多資訊，請參閱[元件](./components.md)文件。