React Hook This article explains how to make network requests using React Hook and what to watch for.
preface
A Hook is a new feature in React 16.8.0. It is also supported in React Native 0.59.0 and later. You can use hooks to use state and similar lifecycle features without using classes. This article uses a simple demo of network request data to further understand the feature of React-hook. The hooks involved include useState, useEffect, useReducer, etc.
Create js pages using useState
Flatlist is used to display a text list page
Const demoHooks = () => {// initialize 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;Copy the code
Use useEffect to request data
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 = () => {// Initial values const [data, setData] = useState({hits: []}); / / side effects useEffect (async () = > {const result = await axios (' https://hn.algolia.com/api/v1/search?query=redux '); setData(result.data); Console. log(' executed ')}); _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;Copy the code
We use the Effect Hook function to get the data, and here we use an AXIos network request framework. UseEffect is called after the render update (componentDidUpdate). Here we call setData in the function to set the interface to return data, triggering the page update mechanism, causing an infinite loop. We just need to do it once after the page has been loaded, which is the class notation for componentDidMount(). UseEffect provides a second parameter to resolve such problems. We pass an empty array [] to make effect hook execute only after Component mount, rather than after Component Update.
/ / side effects useEffect (async () = > {const result = await axios (' https://hn.algolia.com/api/v1/search?query=redux '); setData(result.data); Console. log(' executed ')},[]);Copy the code
The second parameter is the dependency list of Effect Hook. When the data in the dependency changes, the hook will re-execute it. If the dependency is empty, the hook will consider that no data has changed and will not execute it when the component is updated.
The problems you will encounter
An effect function must not return anything besides a function, which is used for clean-up.
Copy the code
Async cannot be used directly in useEffect. If async is added to useEffect, it will disappear.
useEffect(() => { const fetchData = async () => { const result = await axios('https://hn.algolia.com/api/v1/search?query=redux'); setData(result.data); } fetchData(); Console. log(' executed ')},[]);Copy the code
The effect page is as follows
Manually trigger a hook request
Now we implement the manual trigger hook network request, modify the code as follows, add a button, click the button to get the list data with “redux” as the keyword
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 = () => {// initialize const [data, setData] = useState({hits: []}); const [search, UseEffect (() => {const fetchData = async () => {const result = await axios(`https://hn.algolia.com/api/v1/search?query=${search}`); setData(result.data); } fetchData(); Console. log(' executed ')},[]); _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;Copy the code
The second argument to useEffect hook is an empty array, so no effect is triggered. To retrieve the data, add the dependency “search” to the array. After re-running the code, Click the button to see that our data has been updated correctly.
UseEffect () => {const fetchData = async () => {const result = await axios(`https://hn.algolia.com/api/v1/search?query=${search}`); setData(result.data); } fetchData(); Console. log(' executed ')},[search]);Copy the code
Add a load box
Data request is a process, usually when the page requests network data, there will be a friendly loading box, we add a loading state to achieve this.
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 = () => {// initialize 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(' executed ', 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;Copy the code
Network request error handling
Error handling is essential in network requests. Add an error state and use a try/catch for the catch processing.
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(' executed ', isLoading)},[search]);Copy the code
CommonFetchApi
We are going to the code above extracted a generic network request hook is one custom hooks, contains initialData, error, initialState, etc.; A custom hook is also a function within which other hook functions can be called, beginning with “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') // initial 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}}> Network request error... </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;Copy the code
Use useReducer for network requests
The loading,error and initstate of network requests are processed by using useState and useEffect. It can be seen that four useState states are used to process response states. In fact, we can also use the useReducer hook function to implement unified management, which is similar to the react-redux data flow management in class mode. UseReducer can be used to replace useState in many cases. It takes two parameters (state, dispatch) to return a calculated new state, which has the effect of updating the page.
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: 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') // initial 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}}> Network request error... </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;Copy the code
Interrupts the network request while the page is destroyed
Each effect function returns a cleanup function, similar to componentWillUnmount() for removing listeners in class mode. This action is important to prevent memory leaks and other unexpected events. Here we simply provide a Boolean value to clear the network request operation when the component is destroyed.
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]);Copy the code
conclusion
This article uses a web request demo that covers the use of some of the React hooks apis and cautions. These apis are common in development, so you should learn something from this article:
- The use of useState
- UseEffect and precautions
- The use of useReducer
- Custom Hook implementation
The code for this article has been uploaded to Github, Rn-demo
Think the article is good, give me a thumbs up, pay attention to bai!
Technical exchange can pay attention to the public number [Jun Wei said], add my friends to discuss the exchange group: Wayne214 (remarks technical exchange) invite you to join the group, study together to make progress