explanation of useCallback React 

Hi, In this post I will explain about useCallback of React, and for this example we have a Parent component and Child component and we pass a function that get data from some API as a prop of Child component. 

So our function to get data from the API will be called getData() and the parent will use that function to get a specific type of data and also the child component will be using the same function to get other type of data so we need to pass that function in our children prop.

 const getData = (type) => {
    return fetch(`https://jsonplaceholder.typicode.com/${type}`)
      .then(response => response.json())
      .then(json => console.log(json))
  }

Parent component:

// Dependencies
import React, { useState, useEffect } from 'react';

// Components
import Child from './child';

// Styles
import './App.css';

function App() {
  console.log('*** Render the PARENT COMPONENT ***');

  // Local state
  const [time, setTime] = useState(new Date().toString());

  // Function to get data from API
  const getData = (type) => {
    return fetch(`https://jsonplaceholder.typicode.com/${type}`)
      .then(response => response.json())
      .then(json => console.log(json))
  }

  // Just to cause re-rendering
  useEffect(() => {
    // Getting the current date every 1 second
    setInterval(() => {
      setTime(new Date().toString());
    }, 1000);


    // Calling getData function
    getData('todos');

  }, []); // Acting as component did mount

  return (
    <div className="App">

      <section>
        <h1>I'm Parent component</h1>
        <p>{time}</p>

        <Child  getData={getData} />
      </section>
    </div>
  );
}

export default App;

Children component:

// Dependencies
import React, { memo, useEffect } from 'react';

// Functional component
const Child = ({ getData }) => {
  console.log('Render CHILD COMPONENT')

  const divStyle = {
    border: '2px solid green',
    width: '100%',
    height: 'auto'
  }

  useEffect(() => {
    // Executing our getData that is coming from the parent
    getData('users')
  }, [getData]); // Inserting dependencies

  return (
    <>
      <section style={divStyle}>
        <p>I'm Child component</p>
      </section>
    </>
  )
}

// Wrapping the component with memo()
export default memo(Child);

Result: Ok, now our Parent component is using getData to get ‘todos’ data and our Child component is also using getData function to get ‘users’ data. 

If you see the video every time that the Parent component have a re render also the Child component have the re render and with that execute again the getData(‘users’) and this is NOT good for our react application because we can have an infinite loop. 

So, what is happening here is that every time that the Parent component have a render the function getData will be created as new and when we pass that function to the child component the ‘memo’ method say: ok because is new function I’m going to run the useEffect() every single time and that is NOT good.

The useCallback() solve this problem, the first step is import it:

import React, { useCallback } from 'react';

Then wrap our function with useCallback, this going to memoize this function every time that this parent component get a render.

  // Function to get data from API
  const getData = useCallback(type => { 
    return fetch(`https://jsonplaceholder.typicode.com/${type}`)
      .then(response => response.json())
      .then(json => console.log(json))
  }, []);

Our final code will be like this:

Parent component

// Dependencies
import React, { useState, useEffect, useCallback } from 'react';

// Components
import Child from './child';

// Styles
import './App.css';

function App() {
  console.log('*** Render the PARENT COMPONENT ***');

  // Local state
  const [time, setTime] = useState(new Date().toString());

  // Function to get data from API
  const getData = useCallback(type => {
    return fetch(`https://jsonplaceholder.typicode.com/${type}`)
      .then(response => response.json())
      .then(json => console.log(json))
  }, []);

  // Just to cause re-rendering
  useEffect(() => {
    // Getting the current date every 1 second
    setInterval(() => {
      setTime(new Date().toString());
    }, 1000);


    // Calling getData function
    getData('todos');

  }, [getData]); // Acting as component did mount

  return (
    <div className="App">

      <section>
        <h1>I'm Parent component</h1>
        <p>{time}</p>

        <Child  getData={getData} />
      </section>
    </div>
  );
}

export default App;

Child component

// Dependencies
import React, { memo, useEffect } from 'react'; 

// Functional component
const Child = ({ getData }) => {
  console.log('Render CHILD COMPONENT')

  const divStyle = {
    border: '2px solid green',
    width: '100%',
    height: 'auto'
  }

  useEffect(() => {
    // Executing our getData that is coming from the parent
    getData('users')
  }, [getData]); // Dependencies

  return (
    <>
      <section style={divStyle}>
        <p>I'm Child component</p>
      </section>
    </>
  )
}

// Wrapping the component with memo()
export default memo(Child);

Result: If you see now our fetching is happening only one time and NOT more. 

By Cristina Rojas.