React:编写自定义 API 钩子

2025-05-26

React:编写自定义 API 钩子

让我们编写一个方便的自定义反应钩子来处理我们一次又一次编写的常见 API 逻辑。

介绍

离开 React 几年后,我正在重新学习最佳实践。这意味着:Hooks

我们在应用程序中发现的一个非常常见的流程是从 API 加载数据并显示它。

它通常看起来有点像这样:

这很容易导致组件变得非常混乱。让我们利用新学到的 Hooks 知识来解决这个问题。

设计钩子

根据上面描述的流程,定义我们想要钩子提供的数据非常容易。它将返回:

  • 响应数据
  • 正在加载标志
  • 错误(成功则为空)
  • 重试方法

鉴于我仍然欣赏将请求代码委托给服务类,我的想法是让钩子调用服务。

导致以下用法:

const [ user, isLoading, error, retry ] = useAPI('loadUserById', 56);
Enter fullscreen mode Exit fullscreen mode

准备 API 服务

让我们使用一个小服务类,我们可以将所有漂亮的 ajax 代码放在其中。

class APIService {
    async loadUsers() {
        // ... ajax magic
    }

    async loadUserById(id) {
        // ... ajax magic
    }
} 

export default new APIService();
Enter fullscreen mode Exit fullscreen mode

写钩子

我们的目标只是结合标准的反应钩子来创建所有必需的字段。

国家

React 已经为我们提供了useState钩子来创建和更新状态属性。

让我们生成我们的字段:

function useAPI(method, ...params) { 

  const [data, setData]           = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, onError]          = useState(null);

}
Enter fullscreen mode Exit fullscreen mode

调用服务

这里起作用的 React hook 是useEffect,我们可以在其中运行异步代码。

useEffect(() => {
   // ... async code
}, []);
Enter fullscreen mode Exit fullscreen mode

但是,我们决定让钩子返回一个retry方法。因此,让我们将异步代码移到它自己的函数中。

const fetchData = async () => {
   // ... async code
}

useEffect(() => { fetchData() }, []);
Enter fullscreen mode Exit fullscreen mode

现在让我们根据钩子的参数调用正确的服务方法

const fetchData = async () => {
  // Clear previous errors
  onError(null);

  try {
    // Start loading indicator
    setIsLoading(true);

    // Fetch and set data
    setData(await APIService[method](...params));
  } catch (e) {
    // Set the error message in case of failure
    setError(e);
  } finally {
    // Clear loading indicator
    setIsLoading(false);
  }
};

useEffect(() => { fetchData() }, []);
Enter fullscreen mode Exit fullscreen mode

结果

瞧!我们的鱼钩已经准备好了。

function useAPI(method, ...params) {
    // ---- State
    const [data, setData]           = useState(null);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError]         = useState(null);

    // ---- API
    const fetchData = async () => {
      onError(null);
      try {
        setIsLoading(true);
        setData(await APIService[method](...params));
      } catch (e) {
        setError(e);
      } finally {
        setIsLoading(false);
      }
    };

    useEffect(() => { fetchData() }, []);

    return [ data, isLoading, error, fetchData ];
}
Enter fullscreen mode Exit fullscreen mode

在组件中的使用

让我们写一个小例子来说明如何在组件中使用它

function HomeScreen() {
  const [ users, isLoading, error, retry ] = useAPI('loadUsers');

  // --- Display error
  if (error) {
    return <ErrorPopup msg={error.message} retryCb={retry}></ErrorPopup>
  }

  // --- Template
  return (
    <View>
      <LoadingSpinner loading={isLoading}></LoadingSpinner>
      {
          (users && users.length > 0) &&
            <UserList users={users}></UserList>
      }
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

结论

有很多方法可以避免在整个应用程序中重写通用代码。

过去,我经常将其中一些委托给Store,或者用来Mixins创建所有可立即使用的逻辑的组件。

自定义钩子给我们带来了全新的感受,并开辟了解决问题的新策略。

很高兴见证实践的演变。

干杯,

帕特里克

文章来源:https://dev.to/patrixr/react-writing-a-custom-api-hook-l16
PREV
使用 Jest 进行 JavaScript 测试驱动开发 (TDD) 的基础知识
NEXT
我创建了一个 App Store