# 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) {
    // 如果组件不存在，则渲染安装器界面
    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) 文档。