react多个网络请求_如何优雅的在react-hook中进行网络请求

本文介绍了如何在React Hook中进行网络请求,通过使用useState和useEffect实现数据加载、错误处理和加载状态显示。示例中讲解了如何避免无限循环、手动触发请求以及在组件销毁时中断请求。同时还提到了使用useReducer进行更复杂的请求状态管理。
摘要由CSDN通过智能技术生成

本文将介绍如何在使用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(

{item.title}

)

};

return (

data={data.hits}

renderItem={this._renderItem}

/>

);

};

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(

{item.title}

)

};

return (

data={data.hits}

renderItem={this._renderItem}

/>

);

};

export default demoHooks;

我们使用effect hook函数获取数据,这里我们用到了一个[axios](axios/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中使用async,切实报错中也给出了解决方式,就是把async放在useEffect里面,修改如下,重新运行这个警告就消失了。

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(

{item.title}

)

};

_search = () => {

setSearch('redux')

}

return (

Search

data={data.hits}

renderItem={this._renderItem}

/>

);

};

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(

{item.title}

)

};

_search = () => {

setSearch('redux')

}

return (

Search

{

isLoading ?

The Data is Loading ...

:

data={data.hits}

renderItem={this._renderItem}

/>

}

);

};

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(

{item.title}

)

};

_search = () => {

fetchData(`https://hn.algolia.com/api/v1/search?query=${search}`)

}

return (

Search

{

isError &&

网络请求出错了...

}

{

isLoading ?

The Data is Loading ...

:

data={data.hits}

renderItem={this._renderItem}

/>

}

);

};

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(

{item.title}

)

};

_search = () => {

fetchData(`https://hn.algolia.com/api/v1/search?query=${search}`)

}

return (

Search

{

isError &&

网络请求出错了...

}

{

isLoading ?

The Data is Loading ...

:

data={data.hits}

renderItem={this._renderItem}

/>

}

);

};

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的实现

本文对应的代码已上传至Github,wayne214/RnDemo​github.com

觉得文章不错的,给我点个赞哇,关注一下呗!

技术交流可关注公众号【君伟说】,加我好友一起探讨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值