Photo by Dave Phillips on Unsplash
How we use custom hooks?
Design patterns of Custom hooks -> for building large maintainable and scalable production ready applications.
Since the hooks was first announced it keeps revolving, Part of this growth is the custom hooks.
Custom hooks -
"are an essential tool that let you add special, unique functionality to your React applications."
In our team we constantly looking for a better way to build our product, in many terms, scalability, maintenance, user experience, developer experience etc... So we established design patterns to use custom hooks.
We split our usage for custom hooks into 3 different use cases. functional, wrappers and business logic hooks.
Here is the ways we use hooks:
Functional hooks
used as "utils" or "lib" basically just help you to serve some utility function across your project, actually is the most popular usage, 2 examples:useWindowSize -> to get width and height of the window:
useToggle -> to get value and function to toggle it:
Wrappers hooks
Wrapping 3d party packages / library / services -> In order of creating a maintainable project with the level of flexability, We would like to keep our 3d party resources in one place and not spreading it across the whole code base. it will help us in case we would change the resource to some alternative. some examples:useAnalytics -> example of wrapping Mixpanel in a way that easily be replaced with GA:
useCart -> example of wrapping with Recoil in a way that easily be replaced with Redux:
Business logic hooks
similar to MVC pattern in which we spliting our viewer, controller and model. we use custom hooks to manage the entire business logic, keeping the components with the basic logic they require to behave as should. such as:useCalendar:
useProgram:
Cons
One of the biggest traps you should know and avoid, such as each instance of custom hooks has its own state scope. so basically following example will make you trouble:
// usePrograms hook
const usePrograms = () => {
const [programs,setPrograms] = useState([])
const fetchPrograms = async (orgId:number) => {
// will fetch and updated
const data = await getPrograms(orgId)
setPrograms(data)
}
return {programs, fetchPrograms}
}
we expect it to work:
const ProgramsPage = () => {
const {fetchPrograms} = usePrograms()
useEffect(()=>{
fetchPrograms()
},[])
return(
<ProgrmasList/>
)
}
const ProgramsList = () => {
const {programs} = usePrograms()
return (
<>
{
programs.map(program => <Program state={program} key={program.id})/>)
}
</>
)
}
but actually it won't cause we created 2 instances of usePrograms.
1st inside ProgramsPage
and second in ProgramsList
.
we have several ways to solve that:
- pass programs as props
- useContext
- Store such as Redux / Recoil etc...