本文将介绍如何在应用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(备注技术交换)邀你入群,抱团学习共提高