如何优雅的在react-hook中进行网络请求
本文将介绍如何在使用React Hook进行网络请求及注意事项。
前言
Hook是在React 16.8.0版本中新加入的特性,同时在React-Native的0.59.0版本及以上进行了支持,使用hook可以不用class的方式的方式使用state,及类似的生命周期特性。 本片文章通过简单的网络请求数据的demo,来一起进一步认识react-hook这一特性,增加理解,涉及到的hook有useState, useEffect, useReducer等。
使用useState创建js页面
首先创建一个hook的功能页面demoHooks.js, 功能比较简单使用flatlist展示一个文本列表页面
const demoHooks = () => {
// 初始值
const [data, setData] = useState({hits: []});
_renderItem = ({item}) => {
console.log('rowData', item);
return(
<View style={{height: 50, backgroundColor: '#ff0', borderBottomColor: '#f0f', borderBottomWidth: 1, justifyContent: 'center'}}>
<Text style={{height: 20, width: 300}}>{item.title}</Text>
</View>
)
};
return (
<View style={{backgroundColor: '#f5f5f5', marginTop: 20}}>
<FlatList
data={data.hits}
renderItem={this._renderItem}
/>
</View>
);
};
export default demoHooks;
使用useEffect请求数据
import React, {useState, useEffect} from 'react';
import {
Text,
View,
FlatList,
} from 'react-native';
import axios from 'axios'
// import CardView from 'react-native-cardview-wayne'
const demoHooks = () => {
// 初始值
const [data, setData] = useState({hits: []});
// 副作用
useEffect(async () => {
const result = await axios('https://hn.algolia.com/api/v1/search?query=redux');
setData(result.data);
console.log('执行了')
});
_renderItem = ({item}) => {
console.log('rowData', item);
return(
<View style={{height: 50, backgroundColor: '#ff0', borderBottomColor: '#f0f', borderBottomWidth: 1, justifyContent: 'center'}}>
<Text style={{height: 20, width: 300}}>{item.title}</Text>
</View>
)
};
return (
<View style={{backgroundColor: '#f5f5f5', marginTop: 20}}>
<FlatList
data={data.hits}
renderItem={this._renderItem}
/>
</View>
);
};
export default demoHooks;
我们使用effect hook函数获取数据,这里我们用到了一个axios网络请求框架。运行上述代码后,会发现其中的console会一直循环打印,我们知道useEffect函数会在render更新后也就是原来的(componentDidUpdate)进行调用。这里我们在函数中调用了setData设置接口返回数据,触发页面的更新机制,就造成了死循环。 其实我们只是需要再页面加载后执行一次即可,也就是在class写法中componentDidMount()进行数据请求。 useEffect提供了第二参数,用于解决此类问题。这里传入一个空数组[],来让effect hook只在component mount后执行,避免在component update后继续执行。
// 副作用
useEffect(async () => {
const result = await axios('https://hn.algolia.com/api/v1/search?query=redux');
setData(result.data);
console.log('执行了')
},[]);
第二个参数是effect hook的依赖项列表,依赖项中数据发生变化的时候,hook就会重新执行,如果依赖项为空,hook认为没有数据发生变更,在组件更新的时候就不会在此执行。
你会遇到的问题
An effect function must not return anything besides a function, which is used for clean-up.

useEffect(() => {
const fetchData = async () => {
const result = await axios('https://hn.algolia.com/api/v1/search?query=redux');
setData(result.data);
}
fetchData();
console.log('执行了')
},[]);
效果页面如下
手动触发hook请求
现在我们实现手动触发hook网络请求,修改代码如下,加一个按钮,点击按钮后获取以“redux”为关键词的列表数据
import React, {useState, useEffect} from 'react';
import {
Text,
View,
FlatList,
} from 'react-native';
import axios from 'axios'
import { TouchableOpacity } from 'react-native-gesture-handler';
const demoHooks = () => {
// 初始值
const [data, setData] = useState({hits: []});
const [search, setSearch] = useState('')
// 副作用
useEffect(() => {
const fetchData = async () => {
const result = await axios(`https://hn.algolia.com/api/v1/search?query=${search}`);
setData(result.data);
}
fetchData();
console.log('执行了')
},[]);
_renderItem = ({item}) => {
console.log('rowData', item);
return(
<View style={{height: 50, backgroundColor: '#ff0', borderBottomColor: '#f0f', borderBottomWidth: 1, justifyContent: 'center'}}>
<Text style={{height: 20, width: 300}}>{item.title}</Text>
</View>
)
};
_search = () => {
setSearch('redux')
}
return (
<View style={{backgroundColor: '#f5f5f5', marginTop: 20}}>
<TouchableOpacity onPress={this._search}>
<View style={{backgroundColor: '#f00', paddingHorizontal: 10, paddingVertical: 5}}>
<Text>Search</Text>
</View>
</TouchableOpacity>
<FlatList
data={data.hits}
renderItem={this._renderItem}
/>
</View>
);
};
export default demoHooks;
运行上述代码会发现,点击按钮后没有发生任何变化,细心的读者想必已经想到了,在代码中,useEffect hook的第二个参数是空数组,所以没有触发effect运行,重新获取数据,我们添加一下依赖项"search"到数组中,重新运行代码后,点击按钮就可看到我们的数据已经正确更新了。
// 副作用
useEffect(() => {
const fetchData = async () => {
const result = await axios(`https://hn.algolia.com/api/v1/search?query=${search}`);
setData(result.data);
}
fetchData();
console.log('执行了')
},[search]);
添加一个加载框
数据请求是一个过程,通常在页面请求网络数据的时候会有一个友好的提示加载框,我们添加一个loading的state来实现一下。
import React, {useState, useEffect} from 'react';
import {
Text,
View,
FlatList,
} from 'react-native';
import axios from 'axios'
import { TouchableOpacity } from 'react-native-gesture-handler';
const demoHooks = () => {
// 初始值
const [data, setData] = useState({hits: []});
const [search, setSearch] = useState('')
const [isLoading, setIsLoading] = useState(false)
// 副作用
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
const result = await axios(`https://hn.algolia.com/api/v1/search?query=${search}`);
setData(result.data);
setIsLoading(false);
}
fetchData();
console.log('执行了', isLoading)
},[search]);
_renderItem = ({item}) => {
// console.log('rowData', item);
return(
<View style={{height: 50, backgroundColor: '#ff0', borderBottomColor: '#f0f', borderBottomWidth: 1, justifyContent: 'center'}}>
<Text style={{height: 20, width: 300}}>{item.title}</Text>
</View>
)
};
_search = () => {
setSearch('redux')
}
return (
<View style={{backgroundColor: '#f5f5f5', marginTop: 20, flex: 1}}>
<TouchableOpacity onPress={this._search}>
<View style={{backgroundColor: '#f00', paddingHorizontal: 10, paddingVertical: 5}}>
<Text>Search</Text>
</View>
</TouchableOpacity>
{
isLoading ? <View style={{backgroundColor: 'pink', flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text style={{color: '#f00', fontSize: 30}}>The Data is Loading ...</Text>
</View> : <FlatList
data={data.hits}
renderItem={this._renderItem}
/>
}
</View>
);
};
export default demoHooks;
网络请求错误的处理
错误处理是在网络请求中是非常必要的,添加一个error状态,使用try/catch来进行捕获处理。
const [isError, setIsError] = useState(false)
// 副作用
useEffect(() => {
const fetchData = async () => {
setIsError(false)
setIsLoading(true);
try{
const result = await axios(`https://hn.algolia.com/api/v1/search?query=${search}`);
setData(result.data);
}catch(error){
setIsError(true);
}
setIsLoading(false);
}
fetchData();
console.log('执行了', isLoading)
},[search]);
CommonFetchApi
我们将上述代码提取出一个通用的网络请求hook也就是自定义一个hook,包含initialData,error,initialState等;自定义hook也是一个函数,在其内部可以调用其他hook函数,使用“use”开头。
import React, {useState, useEffect} from 'react';
import {
Text,
View,
FlatList,
} from 'react-native';
import axios from 'axios'
import { TouchableOpacity } from 'react-native-gesture-handler';
const useDataApi = (initUrl, initData) => {
const [data, setData] = useState(initData);
const [url, setUrl] = useState(initUrl);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
// 副作用
useEffect(() => {
const fetchData = async () => {
setIsError(false)
setIsLoading(true);
try{
const result = await axios(url);
setData(result.data);
}catch(error){
setIsError(true);
}
setIsLoading(false);
}
fetchData();
},[url]);
return [{data, isLoading, isError}, setUrl];
}
const demoHooks = () => {
const [search, setSearch] = useState('react')
// 初始值
const [{data, isLoading,isError}, fetchData ] = useDataApi(
'https://hn.algolia.com/api/v1/search?query=redux',
{hits: []});
_renderItem = ({item}) => {
return(
<View style={{height: 50, backgroundColor: '#ff0', borderBottomColor: '#f0f', borderBottomWidth: 1, justifyContent: 'center'}}>
<Text style={{height: 20, width: 300}}>{item.title}</Text>
</View>
)
};
_search = () => {
fetchData(`https://hn.algolia.com/api/v1/search?query=${search}`)
}
return (
<View style={{backgroundColor: '#f5f5f5', marginTop: 20, flex: 1}}>
<TouchableOpacity onPress={this._search}>
<View style={{backgroundColor: '#f00', paddingHorizontal: 10, paddingVertical: 5}}>
<Text>Search</Text>
</View>
</TouchableOpacity>
{
isError && <View style={{backgroundColor: 'pink', flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text style={{color: '#f00', fontSize: 30}}>网络请求出错了...</Text>
</View>
}
{
isLoading ? <View style={{backgroundColor: 'pink', flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text style={{color: '#f00', fontSize: 30}}>The Data is Loading ...</Text>
</View> : <FlatList
data={data.hits}
renderItem={this._renderItem}
/>
}
</View>
);
};
export default demoHooks;
使用useReducer进行网络请求
以上通过综合使用useState 和 useEffect的方式实现了网络请求的loading,error,initstate的处理,可以看到我们在其中使用了4个useState处理响应的状态,其实我们也可以通过useReducer这个hook函数,来做统一管理,这里就类似于在class模式下,我们通常使用的react-redux进行数据流管理一样。 useReducer在很多时候可以用来替换useState, 接受两个参数(state, dispatch)返回一个计算后的新state,已达到更新页面的效果。
import React, {useState, useEffect, useReducer} from 'react';
import {
Text,
View,
FlatList,
} from 'react-native';
import axios from 'axios'
import { TouchableOpacity } from 'react-native-gesture-handler';
const fetchDataReducer = (state, action) => {
switch(action.type){
case 'FETCH_INIT':
return{
...state,
isLoading: true,
isError: false
}
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false,
isErroe: false,
data: action.payload,
}
case 'FETCH_ERROR':
return {
...state,
isLoading: false,
isErroe: false,
data: action.payload,
}
break;
default:
return state;
}
}
const useDataApi = (initUrl, initData) => {
const [url, setUrl] = useState(initUrl);
const [state, dispatch] = useReducer(fetchDataReducer,{
data: initData,
isLoading: false,
isErroe: false
})
// 副作用
useEffect(() => {
const fetchData = async () => {
dispatch({type: 'FETCH_INIT'})
try{
const result = await axios(url);
dispatch({type: 'FETCH_SUCCESS', payload: result.data})
}catch(error){
dispatch({type: 'FETCH_ERROR'})
}
}
fetchData();
},[url]);
return [state, setUrl];
}
const demoHooks = () => {
const [search, setSearch] = useState('react')
// 初始值
const [{data, isLoading,isError}, fetchData ] = useDataApi(
'https://hn.algolia.com/api/v1/search?query=redux',
{hits: []});
_renderItem = ({item}) => {
return(
<View style={{height: 50, backgroundColor: '#ff0', borderBottomColor: '#f0f', borderBottomWidth: 1, justifyContent: 'center'}}>
<Text style={{height: 20, width: 300}}>{item.title}</Text>
</View>
)
};
_search = () => {
fetchData(`https://hn.algolia.com/api/v1/search?query=${search}`)
}
return (
<View style={{backgroundColor: '#f5f5f5', marginTop: 20, flex: 1}}>
<TouchableOpacity onPress={this._search}>
<View style={{backgroundColor: '#f00', paddingHorizontal: 10, paddingVertical: 5}}>
<Text>Search</Text>
</View>
</TouchableOpacity>
{
isError && <View style={{backgroundColor: 'pink', flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text style={{color: '#f00', fontSize: 30}}>网络请求出错了...</Text>
</View>
}
{
isLoading ? <View style={{backgroundColor: 'pink', flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text style={{color: '#f00', fontSize: 30}}>The Data is Loading ...</Text>
</View> : <FlatList
data={data.hits}
renderItem={this._renderItem}
/>
}
</View>
);
};
export default demoHooks;
页面销毁时中断网络请求
每个effect函数中都会返回一个函数用于清除操作,类似于class模式中的componentWillUnmount()进行移除监听操作,这个动作很重要,防止发生内存泄露及其他意想不到的情况,这里我们简单提供一个boolean值来在组件销毁时清除网络请求操作。
// 副作用
useEffect(() => {
let doCancel = false;
const fetchData = async () => {
dispatch({type: 'FETCH_INIT'})
try{
const result = await axios(url);
if(!doCancel){
dispatch({type: 'FETCH_SUCCESS', payload: result.data})
}
}catch(error){
if(!doCancel){
dispatch({type: 'FETCH_ERROR'})
}
}
}
fetchData();
return ()=>{
doCancel = true;
}
},[url]);
总结
本文通过一个网络请求的demo讲述了react hooks部分API的使用及注意事项,这几个api也是平时开发工作中常见的,因此通过阅读本文,你应该可以收获如下内容:
- useState的使用
- useEffect的使用及注意事项
- useReducer的使用
- 自定义Hook的实现
觉得文章不错的,给我点个赞哇,关注一下呗! 技术交流可关注公众号【君伟说】,加我好友一起探讨 交流群:wayne214(备注技术交流)邀你入群,抱团学习共进步
关注公众号
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
从四个问题透析Linux下C++编译&链接
摘要:编译&链接对C&C++程序员既熟悉又陌生,熟悉在于每份代码都要经历编译&链接过程,陌生在于大部分人并不会刻意关注编译&链接的原理。本文通过开发过程中碰到的四个典型问题来探索64位linux下C++编译&链接的那些事。 编译原理: 将如下最简单的C++程序(main.cpp)编译成可执行目标程序,实际上可以分为四个步骤:预处理、编译、汇编、链接,可以通过 g++ main.cpp –v看到详细的过程,不过现在编译器已经把预处理和编译过程合并。 预处理:g++ -E main.cpp -o main.ii,-E表示只进行预处理。预处理主要是处理各种宏展开;添加行号和文件标识符,为编译器产生调试信息提供便利;删除注释;保留编译器用到的编译器指令等。 编译:g++ -S main.ii –o main.s,-S表示只编译。编译是在预处理文件基础上经过一系列词法分析、语法分析及优化后生成汇编代码。 汇编:g++ -c main.s –o main.o。汇编是将汇编代码转化为机器可以执行的指令。 链接:g++ main.o。链接生成可执行程序,之所以需...
-
下一篇
SwiftUI直通车系列(3)—— 使用导航
SwiftUI直通车系列三(3)—— 使用导航 关于SwiftUI,我们前两篇博客介绍了独立组件的布局与属性设置相关内容,并且介绍了开发中最常用的列表视图的使用。但是一个完整的应用程序不可能是单界面的,如何使用SwiftUI进行界面间的导航跳转,是我们本博客讨论的重点。前两篇博客地址如下: SwiftUI直通车系列(1)—— 视图的布局与组织 SwiftUI直通车系列(2)—— 列表视图 在本系列的第二篇博客中,我们能够使用SwiftUI来构建列表视图,通常,列表视图用来展示目录页面,当用户点击列表中的某一项时,需要跳转到详情页。在iOS应用中,页面的跳转常用导航控制器进行管理。在SwiftUI中,创建一个导航也是非常简单的,只需要在组件最外层嵌套NavigationView即可。以我们之前的列表视图Demo为例,改写代码如下: import SwiftUI import UIKit struct ContactModel { var name:String var phone:String } let modelData = [ ContactModel...
相关文章
文章评论
共有0条评论来说两句吧...


微信收款码
支付宝收款码