1

I have a scenario where I am to fetch data on a button click and store it in a context variable. Please check the code below:

const [products, setProducts] = useState([]);

 const handleButtonClick= useCallback(() => {
    if (countryCode) {
      async function getproducts() {
        try {
          const apiData = await fetch(URL);
          const jsonData = await apiData.json();
          setProducts(jsonData);
        } catch (error) {
          console.error(error);
        }
      }
      getproducts();
    }
  }, [targetGroup,countryCode]);

I would use this callback function: handleButtonClick() on the button onClick(). I'm still new to React and I'm sometimes confused with React hooks. Is this a correct approach or is there no need for a callback function at all, just a normal function would do?

bazinga
  • 87
  • 2
  • 9

2 Answers2

0

You don't need useCallback for this function, you can write a normal function and use it like this:

<button onClick={handleButtonClick}>Click me</button>

ssunsset
  • 58
  • 7
  • Okay, but the value of URL changes based on ```targetGroup``` and ```countryCode```. So still ```useCallback``` is not required? If not, then could you kindly tell me when is the scenario when ```useCallback``` is to be used? – bazinga Apr 21 '22 at 08:17
  • React is a smart library that is optimised to not need a hook like useCallback in most situations. As for your question, in this case, your handleButtonClick function without useCallback will be re-created on every render and receive a new object reference, but without it, your function will recreate only when the dependencies have changed. – ssunsset Apr 21 '22 at 08:29
  • if you declare an arrow function normally, every time the component rerender it will create a new function. `useCallback` is an hook that can avoid this behavior and allow to create a new function only when something in the dependency list is changed – coglialoro Apr 21 '22 at 08:29
  • @coglialoro Yes, understood. So do you mean to say its not required in such scenarios? – bazinga Apr 21 '22 at 08:35
  • @ssunsset Yes. The fact that you mentioned **As for your question, in this case, your ```handleButtonClick``` function without ```useCallback``` will be re-created on every render** is what made me think if ```useCallback``` would be more efficient – bazinga Apr 21 '22 at 08:35
  • @bazinga Anyway, it's not as black and white as we might think, you should check out [this](https://kentcdodds.com/blog/usememo-and-usecallback) article for more information. Personally, I have never had that of an expensive calculation in a function for me to use useCallback, besides code restructure solves most of the problems on optimization. I would advise you to leave useCallback vs useMemo use for now at least and focus on something else until you really find a way to use them efficiently in the future. – ssunsset Apr 21 '22 at 08:48
  • @bazinga i've made a little example that can help you understand how you can avoid child rerender using `React.memo()` and `useCallback()` : https://stackblitz.com/edit/react-alkxm2?file=src%2FApp.js – coglialoro Apr 21 '22 at 08:54
  • @coglialoro that's a nice example on `memo` and `useCallback` use for future reference of OP. But in a real application, you would mostly send some other state to the Child component which will inevitably cause a re-render every time it changes. And for your simple example, we could have also written `` instead of just sending the function to the Child. – ssunsset Apr 21 '22 at 09:06
  • yes ofc, it was just the simplest example i could think of in order to show the behavior of `useCallback` – coglialoro Apr 21 '22 at 09:07
0

I think your code is okay, but I would like to refactor your code.

import React from 'react';
import { getProducts } from '~/apis/product';

const SomeComponent = () => {
  const [products, setProducts] = React.useState([]);

  const handleButtonClick = React.useCallback(async ()=>{
    if(countryCode) {
       const _products = await getProducts();
       setProducts(_products);
     }
  }, [countryCode]); // I don't know what is countryCode but I guess it must be a state, so you need to add that.

  return (<div>Some Component</div>);
}
export default SomeComponent;

You can separate API function and component. ~/apis/product.js

export async function getproducts() {
  try {
    const apiData = await fetch(`SOME YOUR URL`);
    const jsonData = await apiData.json();
    return jsonData;
  } catch (error) {
    console.error(error);
  }
}
kyun
  • 9,710
  • 9
  • 31
  • 66
  • Thank you! So should I put the API function in another file altogether or maintain it in the same? and I understand it's a best practice method? And ```countryCode``` is a useState variable. – bazinga Apr 21 '22 at 08:43