共计 10503 个字符,预计需要花费 27 分钟才能阅读完成。
本文将介绍如何在应用 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 中应用 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(<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 的实现
本文对应的代码已上传至 Github, RN-DEMO
感觉文章不错的,给我点个赞哇,关注一下呗!
技术交换可关注公众号【君伟说】,加我好友一起探讨
交换群:wayne214(备注技术交换)邀你入群,抱团学习共提高