Hello Developers, All of you know about functional components. Today we will go to the next level in functional components and see in detail how React Hooks allow us to implement variables, functions, and managing states in functional components. Top 5 React Hooks - useState, useEffect, useRef, useMemo, useCallback. We will see each in detail and see how we develop a powerful React app by using Hooks.
React Hooks -
- useState
- useEffect
- useRef
- useMemo
- useCallback
Please download full source code from our GitHub.
useState —
useState hooks allow you to create, update state variables in functional components. Here, we will take two state variables with different data types - Numeric, String and Object and see how it will work.
const [numVal, setNumVal] = useState(95);
const [strVal, setStrVal] = useState('KPITENG');
const [objVal, setObjValue] = useState({
num: numVal,
str: strVal,
});
You see, it’s very simple, here we take the numVal (Numeric) state variable with initial value of 95. Same way I have created strVal (String) state variable with initial value of KPITENG. And objVal (Object) state variable with initial value num (95), str (KPITENG).
Now, Let’s see usage in functional components. Let’s create a TextInput component and assign numeric value from state.
<View>
<Text>Enter Number</Text>
<TextInput
keyboardType="numeric"
placeholder="Enter Number"
value={String(numVal)} // See, how we assign state value
style={Styles.textInput}
/>
</View>
Same way here, we created another TextInput and assigned string value from state.
<View style={Styles.stringContainer}>
<Text>Enter String</Text>
<TextInput
placeholder="Enter String"
value={strVal} // See, how we assign state value
style={Styles.textInput}
/>
</View>
This seems good, Till we have used the State variable, Now, let’s try to update the state variable. Let’s add TextInput onChangeText event and update state Variable.
<View style={Styles.stringContainer}>
<Text>Enter String</Text>
<TextInput
placeholder="Enter String"
onChangeText={(text) => {
setStrVal(text); // update strVal with use input text
setObjValue({...objVal, str: text}); // update str in objVal with user input text
}}
value={strVal}
style={Styles.textInput}
/>
</View>
You see, it’s very simple to update the State variable. Now if you want to see values which you have updated in State, then let’s read values from State and render in Text Component.
<View style={Styles.container}>
<View>
<Text>Enter Number</Text>
<TextInput
keyboardType="numeric"
placeholder="Enter Number"
onChangeText={(text) => {
setNumVal(text);
setObjValue({...objVal, num: text});
}}
value={String(numVal)}
style={Styles.textInput}
/>
<Text style={Styles.stateValue}>Number - {numVal}</Text> // render here
</View>
<View style={Styles.stringContainer}>
<Text>Enter String</Text>
<TextInput
placeholder="Enter String"
onChangeText={(text) => {
setStrVal(text);
setObjValue({...objVal, str: text});
}}
value={strVal}
style={Styles.textInput}
/>
<Text style={Styles.stateValue}>String - {strVal}</Text> // render here
</View>
<View>
<Text>{JSON.stringify(objVal)}</Text> // render here
</View>
</View>
useEffect —
useEffect hooks allow developers to take action on a certain stage of component life cycle and event occured, like First Time Render, Component Update, State Variable || Props Update etc. Let’s see step by step.
useEffect(() => {
console.log(
'useEffect with [] will call one time while component render',
numVal,
);
}, []);
Here, we have created useEffect hooks with empty braces [], which means it is called only when component load. This is similar to componentDidMount integration
But, if you want to call useEffect when numVal (state variable) gets updated. Then, simply add [numValue] in useEffect.
useEffect(() => {
console.log(
'useEffect with [numValue] will call every time while numVal changed',
numVal,
);
}, [numVal]);
This seems perfect, But with this integration useEffect call with two reasons, Initial Component Load and While any changes in state variable in numVal. But, I want to call useEffect only when numVal changed not on Component Load, then,
import {useIsMount} from './useIsMount';
const isMount = useIsMount();
function UseEffectExample() {
const [numVal, setNumVal] = useState(95);
const [strVal, setStrVal] = useState('KPITENG');
const isMount = useIsMount();
function UseEffectExample() {
const [numVal, setNumVal] = useState(95);
const isMount = useIsMount();
useEffect(() => {
if (isMount) {
console.log(
'This console log on first time render, but not on change on numVal',
);
} else {
console.log(
'This console log every time when numVal changed, but not first time',
numVal,
);
}
}, [numVal]);
return (
<View style={Styles.container}>
<View>
<Text>Enter Number</Text>
<TextInput
keyboardType="numeric"
placeholder="Enter Number"
onChangeText={(text) => setNumVal(text)}
value={String(numVal)}
style={Styles.textInput}
/>
<Text style={Styles.stateValue}>Number - {numVal}</Text>
</View>
</View>
);
}
Here, I have added another Hooks useIsMount, which helps to identify that component loaded first time and it’s called second time (due to state variable changes). So I have added the condition if isMount - true which means component load first time. If isMount false - which means it’s getting called due to state variable changes.
This is code for useIsMount hooks:
import { useRef, useEffect } from 'react';
export const useIsMount = () => {
const isMountRef = useRef(true);
useEffect(() => {
isMountRef.current = false;
}, []);
return isMountRef.current;
};
useRef —
useRef hooks returns a mutable ref object. The value will persist for the full lifetime of the component.
Let’s take one example and understand real-time use of useRef. We already have TextInput, now what we will do, we will add one Button (TouchableOpacity) on press of it, we will focus TextInput so the user can directly type it. Let’s do code,
const stringInput = useRef(null);
<TextInput
ref={stringInput}
onChangeText={(text) => setStrVal(text)}
value={strVal}
placeholder="Enter String"
/>
<TouchableOpacity onPress={() => stringInput.current.focus()}>
<Text>Press To Focus Keyboard</Text>
</TouchableOpacity>
See, full code integration for useRef
function UseRefExample() {
const [strVal, setStrVal] = useState('KPITENG');
const stringInput = useRef(null);
return (
<View style={Styles.container}>
<View>
<Text>Enter Number</Text>
<TextInput
ref={stringInput} // assign value to ref (useRef)
placeholder="Enter String"
onChangeText={(text) => setStrVal(text)}
value={strVal}
style={Styles.textInput}
/>
<Text style={Styles.stateValue}>String - {strVal}</Text>
</View>
<View style={Styles.accessNumberInputContainer}>
<TouchableOpacity
onPress={() => {
stringInput.current.focus(); // used ref here, to focus keyboard
console.log('stringInput - ', stringInput);
}}>
<Text>Press To Focus Keyboard</Text>
</TouchableOpacity>
</View>
</View>
);
}
export default UseRefExample;
useMemo —
useMemo hooks helps developers to avoid re-rendering of child components to improve the performance of react application.
Let’s take an example,
const technology = [
{ name: 'React Native', status: true },
{ name: 'React.js', status: true },
{ name: 'Next.js', status: true },
];
const [arrTechnology, setArrTechnology] = useState(technology);
return (
<View style={Styles.container}>
<FlatList
data={arrTechnology}
keyExtractor={(item) => String(item.name)}
renderItem={({item, index}) => (
<UseMemoListItem
item={item}
/>
)}
ItemSeparatorComponent={() => <UseMemoListItemSeprator />}
showsVerticalScrollIndicator={false}
/>
</View>
);
Here, We have created an array to render in FlatList. Let’s see Code of FlatListItem
import {useMemo} from 'react';
function UseMemoListItem({item}) {
return (
<View style={Styles.container}>
<View style={Styles.elementsContainer}>
<Text>{item.name}</Text>
</View>
</View>
);
}
export default UseMemoListItem;
Seems, all good, you can see FlatList rendered Perfectly. But What happens if you have Switch in FlatListItem - onChange of it - you want update status to true/false in arrTechnology then, Then It will re-render all FlatListItems instead of updating/ rendering specific FlatListItem. Which causes performance issues. Think of having 50+ records then each time it will render 50+ FlatListItem. Here, useMemo helps use - useMemo helps to avoid re-rendering by memoizing the value of state variable/ props variable. Let’s see integration
function UseMemoListItem({item, onChange, arrTechnology}) {
return useMemo(() => {
return (
<View style={Styles.container}>
<View style={Styles.elementsContainer}>
<Text>{item.name}</Text>
<Switch onValueChange={onChange} value={item.status} />
</View>
<View>
<Text>{JSON.stringify(arrTechnology)}</Text>
</View>
</View>
);
}, [item.status]);
}
export default UseMemoListItem;
Here, We have wrapped render into useMemo - and tell useMemo to re-render components only when item.status is changed after initial render.
Let’s see full source code -
// src/UseMemoExample/index.js
import * as React from 'react';
import {View, FlatList} from 'react-native';
import {useState} from 'react';
import UseMemoListItem from './UseMemoListItem';
import UseMemoListItemSeprator from './UseMemoListItemSeprator';
import Styles from './Styles';
const technology = [
{ name: 'React Native', status: true },
{ name: 'React.js', status: true },
{ name: 'Next.js', status: true },
];
function UseMemoExample() {
const [arrTechnology, setArrTechnology] = useState(technology);
const onChangeSwitch = (index) => {
let array = arrTechnology;
let object = array[index];
object.status = !object.status;
array[index] = object;
setArrTechnology([...array]);
};
return (
<View style={Styles.container}>
<FlatList
data={arrTechnology}
keyExtractor={(item) => String(item.name)}
renderItem={({item, index}) => (
<UseMemoListItem
arrTechnology={arrTechnology}
item={item}
onChange={() => onChangeSwitch(index)}
/>
)}
ItemSeparatorComponent={() => <UseMemoListItemSeprator />}
showsVerticalScrollIndicator={false}
/>
</View>
);
}
export default UseMemoExample;
useCallback —
useCallback - helps developers to memorize the action - instead of creating separate functions on each render.
In the previous example, we have used onChangeSwitch action, What happens when component render it will render onChangeSwitch, if you have more records in FlatList - due to change component re-render then each time onChangeSwitch instance gets created. To avoid this creation I will use useCallback which will memoize the action.
import {useState, useCallback} from 'react';
const onChangeSwitch = useCallback((index) => {
let array = arrTechnology;
let object = array[index];
object.status = !object.status;
array[index] = object;
setArrTechnology([...array]);
}, []);
You see, we have only returned the useCallBack action - which means this action will create only one time only.
Thanks for reading the article step by step, now you have a detailed idea on how to create a state variable, how to integrate Hooks into a functional component.
Please download full source code from our GitHub.
Thanks for reading Article!
KPITENG | DIGITAL TRANSFORMATION
www.kpiteng.com/blogs | hello@kpiteng.com
Connect | Follow Us On - Linkedin | Facebook | Instagram
Top comments (0)