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:

    Snap (5).png

    useToggle -> to get value and function to toggle it:

    Snap (7).png

  • 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:

    Snap (8).png

    useCart -> example of wrapping with Recoil in a way that easily be replaced with Redux:

    Snap (10).png

  • 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:

    Snap (11).png

    useProgram:

    Snap (13).png

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:

  1. pass programs as props
  2. useContext
  3. Store such as Redux / Recoil etc...

Good Luck and please share you thoughts ✌️