www.apress.com

26/5/20

React Hooks and Components: Custom Hooks as Minions At Your Service

by Oren Farhi

I like simplicity and minimalism - especially when it comes to coding. Mainly aimed at state management for components, React hooks can be creatively used to build up beautifully reusable functional features - and it’s just like reading poetry or listening to a well composed emotional music.

I feel like, once you get to feel and know the building blocks of React Hooks, you can make the code you write much cleaner, organized and DRY.
 

Custom Hooks As A Service (Minions)

The basic useState() hook usually works well for “simple” state management - that can be an object, array, string, boolean etc. The state can be manually set upon request.

I see custom hooks simply as Minions (as in “The Minions”). They are servants of the master - the application. They can be similar or rather, quite different with unique abilities (Playing Guitar like Eddie Van Halen).

Often, for every application there’s a service layer - ie, an api for fetching, updating and deleting data from remote services. These services might be using 3rd party api, http request calls, websockets or others (i.e, firebase, axious, web sockets).

Imagine a Cloud Storage App (i.e, google drive, dropbox). It allows the user to view all files, upload, delete, create groups and much much more.

Take the CloudApi - a service that provides CRUD abilities for files. This is one way to consume and use this service:

         import { CloudApi } from "../api/cloud-api"

          function FilesViewer() {
          const [files, setFiles] = useState([])
          const [file, setFile] = useState(null)
          const [metaData, setMetaData] = useState(null)
          const handleFilesResponse = response => setFiles(response.items)
          CloudApi.fetch(handleFilesResponse)
          const handleSelectedFile = file => setFile(file)
          const handleFileMetaDataResponse = data => setMetaData(data)
          return (
               <section>
                    <button onClick={CloudApi.refresh}>Refresh</button>
                    <button onClick={CloudApi.add}>+ Add</button>
              <section className="file-preview">preview file contents</section>
              <section className="file-metadata">shows selected file's meta data</section>
                    {files.map(file => <File onDelete={CloudApi.delete} />)
              </section>
               )
          }


This code works, but it can be improved. Having 3 useState() in this case, may serve 3 different presentations that can be extracted into their own components.

There are times where some of the code that is presented in the FilesViewer component may be required in other components as well - i.e TrashViewer or TagsViewer.

In the example above the usage of CloudApi is eventually tied to a state that’s presented in the presentation layer.

Instead of repeating this code and in order to make it DRY, we can use a custom hook.

     import { useCloudApi } from "../hooks/use-cloud-api"
      function FilesViewer() {
          const { files, add, remove, fetch, sync } = useCloudApi("files")
          const { preview, setId } = useCloudApi("file")

     fetch()
     return (
          <section>
          <button onClick={fetch}>Refresh</button>
          <button onClick={add}>+ Add New</button>
          <button onClick={sync}>Sync</button>
          <section className="file-preview">

          preview file contents {preview}
          </section>

          {/* some code omitted */}
          {files.map(file => (

          <File onDelete={remove} />
          ))}
          </section>
          )
     }


Still, the initial code can be further improved. There are few sections that can be isolated, and then converted to fully functional standalone units with hooks. We can create a “Minion” that will help us in fetching the files and rendering it. We can create a “Minion” that will help us to preview a file’s content. We can create a “Minion” that will help us to show a file’s metadata.

Composable Hooks

It’s really about creating self-operable units that can be plugged and played very easily - creating a beautiful symphony from brilliant composition.

Restructuring the code like that allows us to craft functional reusable components and minimize duplication.

     function FilesViewer() {
     // some code here...

          return (
          <section>

          <FilePreviewer id={selected} />
          <FileMetadata id={selected} />

          {/* buttons are may be part of <Files /> */}
     <Files />
     <FilesUploader />

     </section>
     )
}

Each component in this code includes a cloud hook and its logics inside - It’s beautifully organized, minimal and self operable. These components are reusable in other views as well - some require the id of a file while the Files is simply a presentation layer of the cloud hook that fetch files and may allow to perform CRUD operations.


     function FilePreviewer({ id }) {
          const { file } = useCloudApi(`file/${id}`)
          return <section>{/* some jsx presenting the file */}</section>
     }
 

Resuable Hooks as Components

Instead of importing hooks across the application code, try to think beyond that - and understand how the state of these hooks is supposed to be used in the app.

I think that in some cases, using a hook in one reusable component may be enough and promotes an elegant way to expose services while using the power of hooks - render the state when it changes.

I think that this strategy promotes the code to be organized and composed:

Component1 => hook1, hook2

Component2 => hook2, hook1


That’s composition. Even though hook1 is used by Component1, its abilities (sync data, deleting data, etc.,) This makes some of the hooks reusable through components.


About the Author

Oren Farhi is a Senior Front End Engineer & Javascript Consultant at Orizens. He consults on how to approach front end development and create maintainable code as well as front end project development by demand.  He studied Computer Science and Management at The Open University. Oren believes in producing easy maintainable code for applications. He follows the principles of reactive programming, best practices of software architecture, and by creates modular and testable code. He likes to keep code and app structure organized to let other developers easily understand and further extend it. Oren is proficient with front end development and is working with various solutions such as Angular, Typescript, ngrx, react, redux, sass, webpack, jasmine, nodejs and JavaScript based Build Tools that solves challenges well. Aside from exploring Web development and blogging, Oren enjoys spending time with his family, playing guitar, meditating, traveling and watching TV series and movies.

This article was contributed by Oren Farhi, author of Reactive Programming with Angular and ngrx.