DEV Community

Cover image for React Hooks
kpiteng
kpiteng

Posted on

React Hooks

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,
});
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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,
  );
}, []);
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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>
 );
}
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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>
  );
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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]);
}, []);
Enter fullscreen mode Exit fullscreen mode

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)