Hooks
Overview
React Native is a popular framework for building cross-platform mobile applications using JavaScript and React. React Native allows developers to use the same codebase for both iOS and Android platforms, saving time and resources.
One of the main features of React Native is the use of components, which are reusable pieces of UI that can have their state and logic. Components can be either class-based or function-based, depending on how they are defined.
In this article, we will focus on function-based components, which are simpler and more concise than class-based components. We will also learn about a new feature of React called Hooks, which allows us to add state and lifecycle methods to function-based components without using classes.
Introduction
Hooks are a new addition to React that was introduced in version 16.8. They are functions that let us “hook into” React features and use them in our function-based components. Hooks make it easier to reuse stateful logic across different components and avoid complex patterns like render props and higher-order components.
Some of the benefits of using Hooks are:
- They reduce the amount of boilerplate code and make the component logic more readable and maintainable.
- They allow us to split the component logic into smaller functions based on related functionality, rather than forcing a split based on lifecycle methods.
- They enable us to use state and other React features without writing a class component, which can be confusing for beginners and inconsistent with the rest of the JavaScript codebase.
What are React Native Hooks?
React Native Hooks are simply Hooks that work with React Native components. They allow us to use state, effects, context, refs, and other React features in our function-based components. React Native Hooks are compatible with both Expo and React Native CLI projects.
There are two types of Hooks in React: built-in Hooks and custom Hooks. Built-in Hooks are provided by React and cover the most common use cases for stateful logic. Custom Hooks are user-defined functions that use built-in Hooks to create reusable functionality.
In this article, we will cover some of the most important built-in Hooks and how to use them in React Native. We will also learn how to create our custom Hooks and share them across different components.
When to use Hooks
Hooks are a powerful tool for adding state and side effects to function-based components, but they are not a replacement for class-based components. There are some cases where class-based components are still preferable or necessary, such as:
- When you need to use lifecycle methods that are not covered by useEffect, such as componentDidMount, componentDidUpdate, componentWillUnmount, etc.
- When you need to access the instance properties of the component, such as this.props, this. State, this. refs, etc.
- When you need to use legacy features that are not compatible with Hooks, such as mixins, decorators, error boundaries, etc.
Therefore, Hooks are not meant to replace class-based components entirely, but rather to complement them and make them easier to use. You can still use class-based components when you need them, but you can also refactor them into function-based components with Hooks when possible.
Rules of Hooks
Hooks are not just regular JavaScript functions. They have some special rules that we need to follow to use them correctly and avoid bugs. The two main rules of Hooks are:
- Only call Hooks at the top level of your component. Don’t call them inside loops, conditions, or nested functions.
- Only call Hooks from React function-based components or custom Hooks. Don’t call them from regular JavaScript functions or class-based components.
These rules ensure that the order of Hook calls is consistent across multiple renders of the component. This is important because React relies on the order of Hook calls to associate the state and effects with the correct component instance.
To help us follow these rules and avoid mistakes, we can use a linting tool called ESLint with a plugin called eslint-plugin-react-hooks. This plugin will warn us if we break any of the rules of Hooks in our code.
React Hooks Installation
To use Hooks in our React Native project, we need to make sure that we have the following dependencies installed:
react: 16.8.0 or higher react-native: 0.59.0 or higher If we are using Expo, we can run expo upgrade to update our dependencies automatically. If we are using React Native CLI, we can run the react-native upgrade or follow the manual instructions here: https://reactnative.dev/docs/upgrading
Once we have updated our dependencies, we can import the built-in Hooks from the react module like this:
We can also create our custom Hooks and import them from other files like this:
useState
useState is the most basic and commonly used Hook. It allows us to add a local state variable to our function-based component and update it with a setter function. The syntax of useState is:
- state is the current value of the state variable. It can be any type of data, such as a string, a number, an object, an array, etc.
- setState is a function that we can use to update the state variable with a new value. It can take either a new value or a function that returns a new value based on the previous state.
- initialState is the initial value of the state variable. It can be any type of data, but it is only used during the first render of the component. If we want to use a complex or expensive calculation to determine the initial state, we can pass a function that returns the initial state instead of the value itself. This way, the function will only run once and not on every render.
Here is an example of how to use useState in a React Native component:
In this example, we have created a simple counter component that displays and updates the count state variable using two buttons. We have used useState to declare and initialize the count variable to 0, and we have used setCount to update it with either count + 1 or count - 1 depending on which button is pressed.
useEffect
useEffect is another important Hook that allows us to perform side effects in our function-based component. Side effects are anything that affects something outside of the scope of the component, such as fetching data from an API, setting up subscriptions or timers, updating document titles, etc.
The syntax of useEffect is:
- effect is a function that contains the code for the side effect. It runs after every render of the component by default, unless we specify a dependency array as the second argument.
- deps is an optional array of dependencies that determines when the effect should run. If we pass an empty array [], the effect will only run once after the first render. If we pass an array with some values [a, b], the effect will only run when either a or b changes. If we omit the dependency array entirely, the effect will run after every render.
- The effect function can also return a cleanup function that runs before the next effect or before the component unmounts. The cleanup function is useful for removing any subscriptions or timers that we have created in the effect.
Here is an example of how to use useEffect in a React Native component:
In this example, we have created a simple clock component that displays and updates the current time using useEffect. We have used useState to declare and initialize the time variable to the new Date(), which returns the current date and time object. We have used useEffect to create a timer that calls setTime with a new Date() every second, updating the time variable.
Custom Hooks
Custom Hooks are user-defined functions that use built-in Hooks to create reusable functionality. Custom Hooks allow us to extract and share common logic across different components, without introducing unnecessary complexity or hierarchy.
The naming convention for custom Hooks is to start with the word use, followed by a descriptive name. For example, useCounter, useFetch, useTheme, etc. This helps us to identify custom Hooks easily and avoid confusion with regular JavaScript functions.
To create a custom Hook, we simply write a function that uses one or more built-in Hooks inside it. We can also pass some parameters to the custom Hook and return some values from it, depending on the functionality we want to achieve.
Here is an example of how to create and use a custom Hook in React Native:
In this example, we have created a custom Hook called useOrientation that returns the current device orientation (portrait or landscape) based on the window dimensions. We have used useState to declare and initialize the orientation state variable, and we have used useEffect to add and remove an event listener for the dimension change event. We have then used the custom Hook in a component `called Orientation that displays the current orientation using a Text component.
Other Important and Useful Hooks
- useRef: This hook allows you to create a mutable reference object that persists across renders. You can use it to access or modify the properties of DOM elements, such as the value of an input or the scroll position of a container. You can also use it to store any value that you don’t want to lose between renders, such as a timer or an interval. To use useRef, you need to import it from React and call it with an initial value. It will return an object with a current property that holds the reference value. You can access or update the current property directly, without triggering a re-render. For example:
- useMemo: This hook allows you to memoize the result of a function and return a cached value unless one of the dependencies has changed. This can be useful for expensive computations or when a component’s rendering depends on the value of a complex data structure. To use useMemo, you need to import it from React and call it with two arguments: a function that returns the value to be memoized, and an array of dependencies that determine when to recompute the value. For example:
- useCallback: This hook allows you to memoize a function and return a cached function reference unless one of the dependencies has changed. This can be useful for optimizing the performance of a component by avoiding unnecessary re-renders caused by passing a new function reference to child components. To use useCallback, you need to import it from React and call it with two arguments: a function that defines the callback, and an array of dependencies that determine when to recreate the callback. For example:
Advantages and Disadvantages
Advantages
- Hooks enable us to reuse stateful logic across different components without changing the component hierarchy or introducing higher-order components or rendering props.
- Hooks make the code cleaner, easier to read, and more modular. We can extract custom hooks that encapsulate related logic and use them in multiple components.
- Hoosimplifiesify the use of external libraries such as react-router, react-redux, and mobx. Hooks provide a more concise and consistent API for accessing data and dispatching actions.
- Hooks eliminate the need for the this keyword and the binding of event handlers. Hooks also avoid common pitfalls of class components such as forgetting to update the state in the constructor or using the wrong lifecycle method.
- Hooks make it easier to test and debug components. We can test custom hooks separately from the components that use them, and we can use the React DevTools to inspect the state and effects of each hook.
Disadvantages
- Hooks introduce a new way of thinking about React components that may be confusing or unfamiliar to some developers. Hooks require us to follow certain rules and conventions, such as not calling hooks inside loops, conditions, or nested functions.
- Hooks can cause performance issues if not used correctly. For example, passing inline functions or objects as dependencies to useEffect or useCallback can cause unnecessary re-rendering or re-computation. To avoid this, we need to use memoization techniques such as useMemo or React.memo.
- Hooks can make the code harder to follow or understand if not structured well. For example, using too many custom hooks in one component can make it difficult to track the flow of data and effects. Also, using hooks that depend on each other can create complex dependencies that are hard to debug.
- Hooks are not compatible with some existing React features or patterns, such as componentDidCatch, getSnapshotBeforeUpdate, or shouldComponentUpdate. To use these features, we still need to use class components or other workarounds.
Best Practices
Some best practices when using Hooks in React Native are:
- Only call Hooks from React functions, such as function components or custom Hooks. Do not call Hooks from regular JavaScript functions or class components.
- Use only one useEffect Hook per component, and use it to handle all the side effects, such as fetching data, updating the document title, or subscribing to events. You can use multiple useEffect Hooks if you want to separate concerns or group related logic together.
- Use Hooks at the top level of your function component, and avoid using them inside loops, conditions, or nested functions. This ensures that Hooks are called in the same order every time the component renders.
- Don’t overuse Hooks or create unnecessary custom Hooks. Only use Hooks when you need to add state or lifecycle methods to your function component, or when you want to reuse some logic across multiple components.
- Use custom Hooks to extract and share common logic between components, such as data fetching, form handling, or animation. Follow the naming convention of starting your custom Hook with us, such as useFetch, useForm, or useAnimation.
- Use the built-in Hooks provided by React Native, such as useColorScheme, useWindowDimensions, or useBackHandler, to access native features and APIs in a declarative way.
- Use the ESLint plugin eslint-plugin-react-hooks to enforce the rules of Hooks and catch common mistakes in your code.
Conclusion
In this article, we have learned about Hooks in React Native and how to use them in our function-based components. We have covered some of the most important built-in Hooks, such as useState, useEffect, useRef, useMemo, and useCallback, and how to create our custom Hooks to reuse stateful logic across different components. We have also learned about some of the rules and best practices for using Hooks correctly and avoiding common pitfalls.
Here are some key points to remember:
- Hooks are functions that let us “hook into” React features and use them in our function-based components.
- Hooks make it easier to reuse stateful logic across different components and avoid complex patterns like render props and higher-order components.
- Hooks are not a replacement for class-based components, but rather a complement to them. We can still use class-based components when we need them, but we can also refactor them into function-based components with Hooks when possible.
- There are two types of Hooks in React: built-in Hooks and custom Hooks. Built-in Hooks are provided by React and cover the most common use cases for stateful logic. Custom Hooks are user-defined functions that use built-in Hooks to create reusable functionality.
- We need to follow some rules when using Hooks, such as only calling them at the top level of our component and only calling them from React function-based components or custom Hooks. We can use ESLint with eslint-plugin-react-hooks to help us follow these rules and avoid mistakes.