React | props inside UseCallBack not updating inside custom hook
Asked Answered
O

1

8

I've build an custom custom post hook that returns the API response and the API post. And I am using useCallback hook to set the Response state

Where it goes wrong is that the Package prop doesn't update inside the useCallback hook.

When I log Package outside the useCallback hook I get the right data inside the propertie. However when I log the Package prop inside the useCallback hook the value of Package doesn't change.

No matter how many times I press the button

I've tried creating an order state that updates everytime the Package prop updates, however whenever I set Package as an value in the scope I get an infinite loop.

I've alos added Package into the scope of the useCallback hook

example

  React.useEffect(() => {
    setOrder(Package);
  }, [Package]);

What I expect to happen is that whenever I call my custom usePostOrder hook the value of Package that is inside the useCallback is always up to date with the latest passed on prop.

CustomHook

/**
 * custom post hook that returns the API response and the API post function
 * @param {string} url
 * @param {object} Package
 * @returns {array} and @param {function}
 */

export const usePostOrder = (url, Package) => {
  const [loading, setLoading] = React.useState(true);
  const [order, setOrder] = React.useState(Package);
  const [response, setResponse] = React.useState({
    config: {
      data: []
    },
    data: {
      id: 0
    }
  });

  console.log("outside func", Package);
  const postOrder = React.useCallback(async () => {
    console.log("inside func", Package);
  }, [url, loading, Package]);

  return [response, postOrder];
};

Answer by Jake Luby with a slight adjustment

/**
 * custom post hook that returns the API response and the API post function
 * @param {string} url
 * @param {object} Package
 * @returns {array} and @param {function}
 */

export const usePostOrder = (url, Package, send) => {
  const [postOrder, setPostOrder] = React.useState();
  const [response, setResponse] = React.useState({
    config: {
      data: []
    },
    data: {
      id: 0
    }
  });

  React.useEffect(() => {
    const getData = async send => {
      //this will have the updated input Package
      await axios
        .post(ApiUrl + url, Package)
        .then(function(response) {
          setResponse(response);
        })
        .catch(function(error) {
          setResponse(error);
          console.log(error);
        });
    };

    send && getData();
  }, [send]); //this will run when url or Package changes

  return [response, postOrder];
};

useAsyncEndpoint.PropTypes = {
  url: PropTypes.url,
  user: PropTypes.object,
  club: PropTypes.object,
  cartItems: PropTypes.array
};

How I call this hook

import {usePostOrder} from "./yourHooksFolder"
  const [send, setSend] = React.useState(false);
  const [response, postOrder] = usePostOrder(
    "url",
    createOrder(user, store, cartItems),
    send
  );

  React.useEffect(() => {
    setSend(false);
  }, [response]);

// send order
  const onGoToPaymentPressed = () => {
    setSend(true);
  };



Outrider answered 8/11, 2019 at 20:19 Comment(0)
L
7

useCallback is not meant to be used like that. It doesn't actually run the function, it simply memoizes it so that between renders the same function isn't recreated.

What you want is the useEffect hook and to have the postOrder as part of the state:

export const usePostOrder = (url, Package) => {
  const [postOrder, setPostOrder] = React.useState()
  const [response, setResponse] = React.useState({
    config: {
      data: []
    },
    data: {
      id: 0
    }
  })


  React.useEffect(() => {
    const getData = async url => {
        //this will have the updated input Package
        console.log(Package) 

        //here is where you'll have your REST calls

        //set your data which will then update the return values in the hook and cause a rerender
        setPostOrder(returnValue)
        setResponse(someResponse)
    }

    getData()
  }, [url, Package]) //this will run when url or Package changes

  return [response, postOrder]
}
Lozano answered 8/11, 2019 at 20:45 Comment(2)
Thanks for your response! I've also decided to pass on a send prop that's a boolean. If this value is true then it will execute the getData() function. Preventing unnecessary requests. Would you recommend this?Outrider
No, I’d recommend using useEffect as it does what you want out of the box without extra listenersLozano

© 2022 - 2025 — McMap. All rights reserved.