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);
};
The key takeaway here is that useCallback returns you a new version of your function only when its dependencies change, saving your child components from automatically re-rendering every time the parent renders.
A bad use case The first problem is that useCallback() hook is called every time MyComponent renders. That reduces the render performance already. The second problem is using useCallback() increases code complexity.
We must remember is that the setState function must not be passed to the useCallback dependency array. The React team suggests this: "React guarantees that setState function identity is stable and won't change on re-renders. This is why it's safe to omit from the useEffect or useCallback dependency list."
shouldComponentUpdate() is used if a component's output is affected by the current change in state or props. The default behaviour is to re-render on every state change. For imitating the functionality of shouldComponentUpdate, we should pass the values in the array through which the component's output gets affected.
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]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With