Tutorials on Custom Hook

Learn about Custom Hook from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

How To Write Your Own Custom Hooks And Share Them - React Hooks 101 Series Part 6 of 6

We have a special React Hooks tutorial on how to create your own custom hooks. This is the 6th and final tutorial on React Hooks, so don't miss it. You will learn how to create custom hooks, which liberates you to implement just about any functionality you might want using React Hooks. You can revisit the full series in case you missed any of the tutorials: If you're ready to take your React development skills to the next level, then we encourage you to look into the full course: The newline Guide to Modernizing an Enterprise React App . This course is taught by Blues Wireless developer, Paige Niedringhaus, who has in-depth experience working with React on large products, and teaching others how to make full use of React's modern features. The course covers: The skills you learn in the course are essential for the needs of many large tech organizations that are actively using React for both client-facing products and internal projects. Get it here . You can also catch Paige live on the React Round Up podcast , where she goes into all things React development.

Thumbnail Image of Tutorial  How To Write Your Own Custom Hooks And Share Them - React Hooks 101 Series Part 6 of 6

Testing a Custom React Hook (useMap)

Developers of the most popular React Hooks libraries rely on tests to enforce the overall quality of their libraries' code. Tests cover a wide variety of different use cases, give developers confidence that everything works as intended and serve as a form of documentation. Anytime a test fails, developers know which set of arguments to use to replicate the bug encountered and squash it lands in the distribution package. When writing a custom React Hook for a library, the Hook should be tested regularly and against the fringest of edge cases to appeal to a greater number of projects. Setting up a testing environment that executes tests fast and reliably requires the proper tools: This testing environment allows you to write tests that closely resemble your user's actions. For example, @testing-library/react comes with methods for rendering a React component ( render ) to a container ( document.body by default) and finding an element within the rendered content of this container via the element's label ( screen.getByLabelText ): With just these two methods, our test mimics, at a high-level, the browser rendering a contact form to the screen and the user searching for an e-mail address input field. In the case of React Hooks, you may not want to create dummy components for the sole purpose of testing your Hooks. A unit test for a React Hook should only test the Hook's functionality (independent of any component calling it). We should reserve the testing of Hooks called within a component for integration tests. Fortunately, the @testing-library/react-hooks library gives us testing utilities for testing Hooks in isolation without having to render any dummy components. Below, I'm going to show you how to test a custom React Hook built for a React Hooks library with the @testing-library/react-hooks library and Jest. To get started, clone this React Hooks library template from GitHub: This template has ESLint, TypeScript and Jest already configured and comes with a custom React Hook, useMap , which we will write tests for. Additionally, the @testing-library/react-hooks library has already been installed as a dev. dependency. The useMap Hook wraps around a Map object and mimics its API. If you would like to learn how to implement this Hook from scratch, then check out the blog post here . Within the __tests__ directory, create a new file, useMap.test.ts . We will write unit tests for the useMap Hook within this file. Within this file, import two methods from the @testing-library/react-hooks library: Then, import the useMap Hook. ( __tests__/useMap.test.ts ) Add a describe block with the text "useMap" to group all tests related to the useMap Hook within this one describe block. ( __tests__/useMap.test.ts ) Alongside the map state variable, which represents the Hook's current Map object, the useMap Hook provides several action methods for updating this state variable: From this point on, all describe blocks and tests will be written within the useMap describe block. Let's write a describe block for the set action method that covers two cases involving this action method: ( __tests__/useMap.test.ts ) For the "should update an existing key-value pair," we should render the useMap Hook using the renderHook utility method from the @testing-library/react-hooks library: ( __tests__/useMap.test.ts ) Here, we call the Hook with an array of one key-value pair. We pass this to a new Map object to create the initial map . The renderHook method returns an object with a result field. This field is a React ref. By reading the ref's current field, you can access the Hook's API (an array that contains the map state variable and the action methods). For now, let's omit the map state variable. I will explain why later on. ( __tests__/useMap.test.ts ) Let's double-check that our initial state was set correctly to a Map object with the key-value pair 1: "default" . ( __tests__/useMap.test.ts ) Save the changes. In the terminal, run the test npm script to verify that the initial state is set correctly: Now, let's call the set action to update the key-value pair of 1: default to 1: changed . We need to call the set action within the act method to flush any changes to the state into the simulated DOM before running any subsequent assertions. ( __tests__/useMap.test.ts ) Check that the key-value pair has been updated. The 1 key should have a corresponding value of "changed" . ( __tests__/useMap.test.ts ) Once again, save the changes. Run the test npm script to verify that the state has correctly changed: ( __tests__/useMap.test.ts ) With these few steps, you can write tests for the remaining action methods: ( __tests__/useMap.test.ts ) Remember how we omitted the map state variable and only accessed it from result.current[0] ? This is because all action methods are immutable. Therefore, anytime we call any one of these action methods, the map state variable destructured from the initial result.current will reference the initial Map object it's set to, not the new Map object that set it to internally in the Hook. Essentially, after the action method call, result.current references a completely different Map object than the one referenced by the destructured map state variable. Let's add a new describe block labeled "hook optimizations." Inside of this describe block, write a test to confirm this behavior: ( __tests__/useMap.test.ts ) Finally, let's write a test to make sure our useCallback and useMemo optimizations maintain reference equality after state changes. The reference to the action methods should never change. ( __tests__/useMap.test.ts ) Run the tests one final time and watch them all pass! Altogether... ( __tests__/useMap.test.ts ) For a final version of this code, visit the GitHub repository here . Try testing your own custom React Hooks with the @testing-library/react-hooks library.

Thumbnail Image of Tutorial Testing a Custom React Hook (useMap)

I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

This has been a really good investment!

Advance your career with newline Pro.

Only $30 per month for unlimited access to over 60+ books, guides and courses!

Learn More

Writing a Custom React Hook for Spotify's Web API (Implicit Grant Flow)

As one of the most popular music streaming services, Spotify encourages developers to craft new and engaging experiences for music lovers around the world. With over seventy endpoints, Spotify's Web API allows external applications to access Spotify's vast catalog of albums, artists, playlists, tracks and podcasts and detailed profile information about the current authorized user. All of this data opens the door to building applications that explore and analyze the data, generate new insights from the data or present the data in exciting new ways. Want to discover new artists based on the artists of the tracks in your playlists? Use the GET https://api.spotify.com/v1/artists/{artist_id}/related-artists endpoint to fetch artists related to an artist based on the analysis of other users' listening histories. Want to re-imagine the user interface for searching and displaying tracks that match a query? Use the GET https://api.spotify.com/v1/search endpoint to query and retrieve results from Spotify's tracks catalog. If you decide to create a Spotify-connected application with React , then centralizing all Spotify-related logic inside of a single, reusable custom hook, named useSpotify , eases the integration of Spotify into the application. This organizes everything Spotify-related in one place. For a component to communicate with Spotify's Web API, just import the useSpotify hook and call the hook within the component. Whenever Spotify updates its API with new endpoints or modified response objects, you edit one single file in the codebase: useSpotify.js . The hook enables all components to get the authorization status of the user and interact with Spotify's Web API. Below, I'm going to show you... Imagine building a dashboard that features a view for searching tracks from Spotify's catalog, a view for managing the current authorized user's saved albums and tracks, etc. For the custom hook useSpotify to support this type of application, it must handle: To begin, let's briefly recap React hooks and how they work. Hooks provide us a way to reuse stateful logic. When multiple components use the same hook, the state within the hook is not shared by those components. Therefore, we will need a provider component to wrap the application and make the hook's object available to any child component that calls the hook with context. Anytime the user decides to "log in" or "log out" (represented as having or not having a valid access token respectively), the changes to the hook's state will cause those components to re-render. Here's the base template of the hook, which exports the provider component <SpotifyComponent /> and a hook that returns the current context value, which comes from the object passed to the value prop of the provider component. ( useSpotify.js ) All of the Spotify-related logic will reside within the useProvideSpotify provider hook, which creates the spotify object that's fed to the provider component, exposes methods for working with Spotify's Web API and manages state variables like token and user . To obtain authorization, the hook will implement methods for facilitating the Implicit Grant Flow . Although the Implicit Grant Flow does not provide a refresh token, it requires only the client-side application to carry out the entire authorization flow. No additional backend code needed! Upon being granted authorization, Spotify issues a short-lived access token that expire within 3600 seconds (1 hour). Once the token expires and is no longer valid, you must complete the entire flow again to obtain a new access token. To start, let's write a login method that opens a popup that prompts the user to... ( useSpotify.js ) Upon authorization, the user is redirected to the redirect URI (e.g. http://localhost:3000/callback ) within the popup. The redirect URI contains a hash fragment with the access token, its type, its expiration time period and state (e.g. http://localhost:3000/callback#access_token=xxxx&expires_in=3600&token_type=Bearer&state=xxxx ) encoded as a query string. When this page loads and its component is mounted, extract the access token and its metadata from the query string and store the token and its expiration timestamp in local storage. Then, call the main window's spotifyAuthCallback method, which was registered back in the login method, to close the popup and update the hook's state with these values. ( useSpotify.js ) By storing the token in local storage, we don't need to constantly ask the user to re-authorize themself if they decide to close the browser, reopen it and revisit the application less than an hour later. And, if the user does revisit the application more than an hour later, then knowing the expiration timestamp makes it possible to invalidate the token and ask agains for authorization. ( useSpotify.js ) Note : Any value stored in local storage is stringified and must be converted back to its appropriate typing. Note : Yes, it's not recommended to store access tokens in local storage due to cross-site scripting (XSS) and possibly being read by malicious JavaScript code. Only do this for demos. For production-grade applications, I recommend going with a different authorization flow (one that involves the server-side and setting the token within an HTTP-only cookie). Anytime the user decides to "log out," invalidate the token and reload the page so that it automatically redirects them to the homepage. ( useSpotify.js ) To restrict the redirect URL to only be visited within the popup by Spotify, let's write a method to check whether the redirect occurs within a "valid" popup that originated from the application itself. ( useSpotify.js ) The useHistory hook from react-router-dom gives a history instance to verify its length is at least two (one entry for Spotify, the other for the redirect). Once the token and expiration timestamp state variables are updated, the hook should automatically fetch the current authorized user's information from Spotify and update the state with this information. ( useSpotify.js ) For the user to be considered "logged in," the hook's state must the token and user's information, and the token cannot be expired. ( useSpotify.js ) To get data from Spotify's Web API, define a method for each endpoint. For this hook, we will only define two methods: one for the GET /me endpoint and another for the GET /search endpoint. Any request sent to the API must have the access token added to its Authorization header to allow Spotify to identify the user and know that the requesting application has permission from the user to access to their data. ( useSpotify.js ) The callEndpoint method serves as a helper method that keeps the endpoint methods' definitions DRY. For more endpoints, visit Spotify's Web API reference here . When the user performs a full-page refresh of the application, it takes time for the application to fetch the user's information. The hook should provide a flag that notifies components when the application is "loading" so that they can present a loading indicator to the user. The application is in a loading state when the hook is initialized, and it is finished loading when it has successfully (or unsuccessfully) fetched the user's information from Spotify. ( useSpotify.js ) The hook will expose the following state variables and methods to child components that call it: ( useSpotify.js ) Using getters allows the component to reference the method call like a flag. So instead of calling hasLoggedIn() , just use hasLoggedIn in the component. Altogether... ( spotify.js ) ( .env ) ( useSpotify.js ) Want to see how this hook works in an actual application? Clone the demo application here and run it locally. Try using this hook for your next Spotify-connected application!

Thumbnail Image of Tutorial Writing a Custom React Hook for Spotify's Web API (Implicit Grant Flow)