Should we use utility functions in react or should everything be components and hooks?
I had this scenario:
Utility function for formatting money.
const formatMoney = (value) => value == null ? '' : '$' + value.toFixed(2)
This was used in more than 200 places. Everything was good, until the new requirement came. Now we were supposed to use dynamic currency, not only dollar.
So, the function formatMoney became dependent of the currently logged user in order to display his currency.
Few thigs that I could do:
- add second parameter to the function and pass it everywhere manually
- convert the function into hook/component, so it can get what it needs using hooks, since the logged user is kept in global provider.
The second variant was better, since the information that the function needs is a constant, won’t be changed with something else. It will always come from the current user.
But both of those variants requires to manually refactor all the places that this function was used
The idea of a variable/function is to reuse some logic and it was doing it until it became dependent of something that it can’t get itself, since functions can’t call hooks.
I ended up with something like this
const useFormatMoney = (value) => { // or a component
const user = useUserContext()
return value == null ? '' : user.currency + value.toFixed(2)
}
I wasn’t able to do global replace, because now it has a react rules to follow, it should not be called conditionally, while before it could. After refactoring all those 200+ places, it made me rethink:
Every utility function one day can become dependent of something that is not able to get itself. But if I always use components/hooks they can always read things from providers and would not require to manually refactor all places that are used. So,
Is using utility functions a bad practice? Should we always use hooks/components?
Oktay Yuzcan is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
Is using utility functions a bad practice?
No.
Even if you had named your original function useFormatMoney
, it wouldn’t have been a hook. It would have looked like a hook, and you might have used it like a hook, but it still would have been an ordinary function.
You could instead write a hook that returns a function
const useFormatMoney = () => {
const user = useUserContext()
return (value) => value == null ? '' : user.currency + value.toFixed(2)
}
This can be called unconditionally, and then the resulting function can be called conditionally. It is a much easier change, you can even keep the name formatMoney
at the call sites.
N.b. a refactor is a change in code which keeps the observable behaviour the same, which is not the case here, the new requirement needs a change in behaviour.
6