React:编写自定义API钩子
让我们编写一个方便的自定义反应钩子来处理我们一次又一次编写的常用API逻辑。
介绍
离React几年后,我正在重新培养自己的最佳实践。这意味着:钩子
我们在应用程序中发现的非常(非常)常见的流程之一是从API加载数据并显示它。
它通常看起来像这样:
这倾向于导致非常混乱的组件。让我们使用我们新发现的钩子知识来解决这个问题。
设计钩子
基于上面描述的流程,定义我们希望钩子提供的数据非常容易。它将返回:
- 响应数据
- 加载标志
- 错误(成功时失效)
- 重试方法
鉴于仍然希望将请求代码委托给服务类,我的想法是让钩子调用服务。
导致以下用法:
const ( user, isLoading, error, retry ) = useAPI('loadUserById', 56);
准备API服务
让我们使用一个小服务类,我们可以在其中放置所有漂亮的ajax代码。
class APIService { async loadUsers() { // ... ajax magic } async loadUserById(id) { // ... ajax magic } } export default new APIService();
写钩子
我们的目标只是结合标准反应挂钩来创建我们所有必需的字段。
国家
React已经使用useState钩子来创建和更新状态属性。
让我们生成我们的字段:
function useAPI(method, ...params) { const (data, setData) = useState(null); const (isLoading, setIsLoading) = useState(false); const (error, onError) = useState(null); }
致电服务
这里发挥的React钩子是useEffect,我们可以在其中运行我们的异步代码。
useEffect(() => { // ... async code }, ());
但是,我们已经决定钩子会返回一个 retry
方法。所以让我们将异步代码移动到它自己的函数中
const fetchData = async () => { // ... async code } useEffect(() => { fetchData() }, ());
现在让我们根据钩子的参数调用正确的服务方法
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() }, ());
结果
瞧我们的钩子已准备好消费。
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 ); }
在组件中的用法
让我们写一个关于如何在组件中使用它的例子
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> ); }
结论
有许多方法可以避免在应用程序中重写公共代码。
在过去,我经常将其中一些委托给a Store
,或使用 Mixins
创建可以使用所有逻辑的组件。
定制挂钩为我们带来了全新的味道,并为处理问题开辟了新的策略。
很高兴见证实践的演变。
干杯,
帕特里克