Supercharging Your React Native Apps with Hooks

Supercharging Your React Native Apps with Hooks

Riddhesh Ganatra Profile Picture
Riddhesh Ganatra Co-founder at Code Bauthor linkedin
Published On
Updated On
Table of Content
up_arrow

Introduction

Have you ever wondered how to make your React Native apps more efficient and easier to build? Enter React Native Hooks - a game-changer in mobile app development.

React Native Hooks are like little helpers that make your life as a developer easier. They allow you to use state and other React features more simply, especially in functional components.

Whether you're a beginner or an experienced developer, understanding and using React Native Hooks can take your app development skills to the next level. So, let's dive in and explore the power of React Native Hooks together!

What are React Native Hooks?

React Native hooks are functions that let you "hook into" React state and lifecycle features from function components. Before hooks, these features were only available in class components, making functional components more limited in their capabilities. React Native hooks provide a way to use state, context, and other React features without writing a class.

Imagine you're building a house with LEGO bricks. Each brick represents a small piece of your app's code. Traditionally, when you wanted to add a special feature or change something, you had to take apart big sections of your LEGO house and rebuild them, which could get messy and time-consuming.


React Native Hooks are like magical connectors that allow you to attach new LEGO pieces without dismantling everything. In other words, they're tools that make adding functionality to your app easier without rewriting a bunch of code.

For example, let's say you want your app to remember a user's name. With React Native Hooks, you can use a special hook called useState to create a space in your app's memory where you can store and update the user's name easily, without messing up the rest of your code.

Hooks help keep your code neat and organized, like sorting your LEGO pieces into different compartments so you can find them quickly when you need them. Plus, they make it easier to reuse code and add new features, saving you time and effort in the long run.

So, in simple terms, React Native Hooks are tools that make it easier and more efficient to build and manage your app's code, like magic LEGO connectors for your digital creations!

List of React Native Hooks

React Native provides developers with a powerful set of hooks to enhance the functionality and maintainability of their applications. Hooks are functions that allow developers to use React features like state and lifecycle methods in functional components.

all types of hooks in react native

Basic hooks

The three basic React Native hooks are:

1. useState

useState is one of the most fundamental hooks in React Native. It allows functional components to manage local state without needing to convert them into class components. With useState, you can declare state variables and update them within your functional components.

Syntax

const [stateVariable, setStateFunction] = useState(initialValue);


Usage

  1. Setting Up State
    You can call useState to declare a state variable within a functional component. This initializes the state variable with the provided initial value.
  2. Accessing State
    After declaring a state variable using useState, you can access its current value directly by referencing the variable returned by useState.
  3. Updating State
    To update the state variable, you call the setStateFunction provided by useState, passing the new value as an argument. React will then re-render the component with the updated state.

Example

import React, { useState } from 'react';
import { Button, Text, View } from 'react-native';
const Counter = () => {
  // Declare a state variable 'count' with initial value 0
  const [count, setCount] = useState(0);
  return (
    <View>
      <Text>Count: {count}</Text>
      <Button
        title="Increment"
        onPress={() => setCount(count + 1)} // Update 'count' by incrementing it by 1
      />
      <Button
        title="Decrement"
        onPress={() => setCount(count - 1)} // Update 'count' by decrementing it by 1
      />
    </View>
  );
};
export default Counter;


In this example, useState is used to declare a state variable named count with an initial value of 0. Two buttons are rendered—one for incrementing the count and the other for decrementing it. Pressing these buttons triggers the setCount function with the updated value, causing the component to re-render with the new count value.

2. useEffect

useEffect is a hook in React Native that allows functional components to perform side effects. Side effects can include things like fetching data, subscribing to events, or manually changing the DOM. useEffect replaces lifecycle methods such as componentDidMount, componentDidUpdate, and componentWillUnmount in class components.

Syntax

useEffect(() => {
// Side effect code here
return () => {
// Cleanup code here (optional)
};
}, [dependencies]);


Parameters

  • Effect Function: The first parameter is a function that contains the code for the side effect you want to perform. This function will run after every render, including the initial render.
  • Dependencies Array: The second parameter is an optional array of dependencies. It allows you to specify which values the effect depends on. If any of the values in the dependencies array change between renders, the effect function will re-run.

Usage

1 . Performing Data Fetching
You can use useEffect to fetch data from an API when the component mounts or when certain props change.

  1. Subscribing to Events
    useEffect can be used to subscribe to events such as keyboard events or WebSocket messages.
  2. Managing Document Titles
    It's possible to use useEffect to update the document title dynamically based on the state of your application.
  3. Cleaning Up Resources
    The cleanup function returned by useEffect allows you to perform cleanup tasks when the component unmounts or when the dependencies change. This can include unsubscribing from event listeners or canceling network requests.


Example

import React, { useState, useEffect } from 'react';
import { Text, View, Button } from 'react-native';
const ExampleComponent = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    // Update the document title with the current count
    document.title = `Count: ${count}`;
    // Cleanup function
    return () => {
      // Reset the document title when the component unmounts
      document.title = 'React App';
    };
  }, [count]); // Only re-run the effect if count changes
  return (
    <View>
      <Text>Count: {count}</Text>
      <Button onPress={() => setCount(count + 1)} title="Increment" />
    </View>
  );
};
export default ExampleComponent;


In this example, useEffect is used to update the document title dynamically based on the count state variable. The effect function runs after every render, but it only re-runs if the count value changes. The cleanup function resets the document title when the component unmounts.


3. useContext

useContext is a hook in React Native that allows functional components to consume context created by a Context.Provider component. Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Syntax

const value = useContext(MyContext);


Parameters

  • MyContext: This parameter is a reference to the context object created by React.createContext(). It represents the context that you want to access within the component.

Usage

  1. Consuming Context
    Use useContext inside a functional component to access the value provided by the nearest Context.Provider component in the component tree.

Example

Let's consider an example where a theme context is created and used in a functional component:

import React, { useContext } from 'react';
import { View, Text, StyleSheet } from 'react-native';
// Create a context for the theme
const ThemeContext = React.createContext('light');
// A component that provides the theme context
const ThemeProvider = ({ children }) => {
return (
<ThemeContext.Provider value="dark">
{children}
</ThemeContext.Provider>
);
};
// A component that consumes the theme context
const ThemedComponent = () => {
const theme = useContext(ThemeContext);
return (
<View style={styles[theme]}>
<Text style={styles.text}>Themed Component</Text>
</View>
);
};
// Styles for different themes
const styles = StyleSheet.create({
light: {
backgroundColor: '#ffffff',
color: '#000000',
padding: 10,
},
dark: {
backgroundColor: '#000000',
color: '#ffffff',
padding: 10,
},
text: {
fontSize: 20,
},
});
// Wrap the ThemedComponent with the ThemeProvider
const App = () => {
return (
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
);
};
export default App;


In this example

ThemeContext is created using React.createContext('light'), with a default value of 'light'.

ThemeProvider provides the theme context with a value of 'dark'.

ThemedComponent consumes the theme context using useContext(ThemeContext).

Based on the current theme value, the component renders with different styles.

Using useContext, the ThemedComponent can access the theme value provided by the nearest ThemeProvider ancestor without the need for prop drilling. This simplifies the code and makes it more maintainable.

Additional hooks

In addition to the basic hooks, React Native also provides several additional hooks that offer specialized functionalities:

1. useReducer

useReducer is another essential hook in React Native for managing more complex state logic. It's particularly useful when state transitions follow a predictable pattern, such as those found in state machines or data structures like trees and graphs. useReducer is akin to useState but offers more control over state updates, especially in scenarios where the next state depends on the previous one.

Syntax

const [state, dispatch] = useReducer(reducer, initialState);


Parameters

  • reducer: A function that determines how the state should be updated based on the dispatched action.
  • initialState: The initial state value.


Usage

  1. Setting Up Reducer
    You define a reducer function that specifies how the state should change in response to dispatched actions.
  2. Initializing State
    When using useReducer, you provide an initial state value. This state can be a simple value, an object, or an array.
  3. Dispatching Actions
    To update the state, you dispatch actions to the reducer function. The reducer then determines how to modify the state based on the action type and payload.


Example

import React, { useReducer } from 'react';
import { Button, Text, View } from 'react-native';
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<View>
<Text>Count: {state.count}</Text>
<Button
title="Increment"
onPress={() => dispatch({ type: 'increment' })}
/>
<Button
title="Decrement"
onPress={() => dispatch({ type: 'decrement' })}
/>
</View>
);
};
export default Counter;


In this example, useReducer is utilized to manage the state of the count. A reducer function is defined to handle actions of type 'increment' and 'decrement', modifying the count value accordingly. The initial state is provided as an object with a count property set to 0. Pressing the increment or decrement button dispatches the corresponding action to the reducer, which updates the state accordingly.

2. useCallback

useCallback is a hook used for memoizing functions in React Native. It is particularly helpful in optimizing performance by preventing unnecessary re-renders of child components that rely on these functions. useCallback returns a memoized version of the provided function that only changes if one of the dependencies has changed.

Syntax

const memoizedCallback = useCallback(
() => {
// Function logic
},
[dependencies]
);


Parameters

  • Function: The function that you want to memoize.
  • Dependencies: An array of values. useCallback will only re-create the memoized function if any of these values change.


Usage

  1. Memoizing Functions
    useCallback takes a function as its first argument and returns a memoized version of that function.
  2. Specifying Dependency
    You can provide an array of dependencies as the second argument to useCallback. The memoized function will only be re-created if any of these dependencies change.
  3. Optimizing Performance
    useCallback is useful when passing callbacks to child components to prevent unnecessary re-renders caused by the creation of new function references on each render.


Example

import React, { useState, useCallback } from 'react';
import { Button, View } from 'react-native';
const MemoizedButton = React.memo(({ onPress }) => {
console.log('Rendering MemoizedButton');
return <Button title="Click Me" onPress={onPress} />;
});
const Counter = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
const decrement = useCallback(() => {
setCount((prevCount) => prevCount - 1);
}, []);
return (
<View>
<MemoizedButton onPress={increment} />
<MemoizedButton onPress={decrement} />
</View>
);
};
export default Counter;


In this example, useCallback is used to memoize the increment and decrement functions. These memoized functions are then passed as props to the MemoizedButton component. By memoizing these functions, we ensure that they remain the same reference across re-renders as long as their dependencies (in this case, none) remain unchanged, thus optimizing performance.

3. useMemo

useMemo is a hook in React Native used for memoizing the result of expensive computations so that they are only recalculated when necessary. It is particularly useful for optimizing performance by preventing unnecessary re-renders caused by expensive calculations within functional components.

Syntax

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);


Parameters

  • Function: The function that computes the value you want to memoize.
  • Dependencies: An array of values. useMemo will recompute the memoized value only if any of these dependencies have changed.


Usage

  1. Memoizing Values
    useMemo takes a function as its first argument, which computes the value you want to memoize.
  2. Specifying Dependencies
    You can provide an array of dependencies as the second argument to useMemo. The memoized value will only be recomputed if any of these dependencies change.
  3. Optimizing Performance
    useMemo is beneficial when you have expensive computations that depend on certain values and you want to avoid recalculating them unnecessarily on each render.

Example


import React, { useState, useMemo } from 'react';
import { Text, View } from 'react-native';
const computeFactorial = (number) => {
console.log('Computing factorial of', number);
let factorial = 1;
for (let i = 1; i <= number; i++) {
factorial *= i;
}
return factorial;
};
const FactorialDisplay = ({ number }) => {
const factorial = useMemo(() => computeFactorial(number), [number]);
return (
<View>
<Text>Factorial of {number} is: {factorial}</Text>
</View>
);
};
const App = () => {
const [number, setNumber] = useState(5);
return (
<View>
<FactorialDisplay number={number} />
<Text>Change number:</Text>
<Text>{number}</Text>
<Text onPress={() => setNumber(number + 1)}>Increment</Text>
<Text onPress={() => setNumber(number - 1)}>Decrement</Text>
</View>
);
};
export default App;


In this example, useMemo is used to memoize the factorial of a number. The compute factorial function is memoized using useMemo, and it is recalculated only when the number changes. This optimization prevents unnecessary recalculations of the factorial on each render, improving the performance of the application.


4. useRef

useRef is a hook in React Native used for creating a mutable reference that persists across renders. It is commonly used to access and interact with DOM elements or to store mutable values that do not trigger re-renders when they change.

Syntax

const refContainer = useRef(initialValue);


Usage

  1. Creating a Reference
    You call useRef with an optional initial value to create a mutable reference. The returned ref object has a current property that holds the current value of the reference.
  2. Accessing the Reference
    You can access the current value of the reference using the current property of the ref object.
  3. Updating the Reference
    Changing the value of the ref object's current property does not trigger a re-render of the component.


Example


import React, { useRef, useEffect } from 'react';
import { Button, Text, View, TextInput } from 'react-native';
const FocusableTextInput = () => {
const inputRef = useRef(null);
useEffect(() => {
// Focus on the input element when the component mounts
inputRef.current.focus();
}, []);
return (
<View>
<Text>Enter your name:</Text>
<TextInput ref={inputRef} />
</View>
);
};
const App = () => {
const renderCount = useRef(0);
useEffect(() => {
renderCount.current += 1;
});
return (
<View>
<Text>Render count: {renderCount.current}</Text>
<FocusableTextInput />
</View>
);
};
export default App;


In this example, useRef is used to create two different kinds of references:

inputRef is used to reference the TextInput component, allowing us to focus on it when the component mounts.

renderCount is used to keep track of the number of renders of the App component without causing re-renders. It is updated inside a useEffect hook, which runs after every render. Since changing the current property of a ref object does not trigger a re-render, the render count is updated without causing an infinite loop.

5. useImperativeHandle

useImperativeHandle is a hook in React Native that allows you to customize the instance value that is exposed when using React.forwardRef. It is typically used in conjunction with React.forwardRef to expose certain methods or properties of a child component to its parent component.

Syntax

useImperativeHandle(ref, createHandle, [deps])


Parameters

  • ref: A ref object created with useRef or passed from the parent component using React.forwardRef.
  • createHandle: A function that returns an object with properties and methods to be exposed via the ref.
  • deps (optional): An array of dependencies. The hook will re-run if any of these dependencies change.


Usage

  1. Customizing Instance Value
    useImperativeHandle allows you to customize the instance value exposed by a child component when using React.forwardRef.
  2. Defining Exposed Methods or Properties
    Inside the createHandle function, you define the properties and methods that you want to expose via the ref object.
  3. Optimizing Performance
    You can specify dependencies to control when the hook should re-run and update the exposed instance value.


Example

import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react';
import { Button, View, Text } from 'react-native';
// Child component
const ChildComponent = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
// Expose increment method via ref
useImperativeHandle(ref, () => ({
increment() {
setCount(count + 1);
}
}));
return (
<View>
<Text>Count: {count}</Text>
</View>
);
});
// Parent component
const ParentComponent = () => {
const childRef = useRef(null);
const handleIncrement = () => {
childRef.current.increment();
};
return (
<View>
<ChildComponent ref={childRef} />
<Button title="Increment from Parent" onPress={handleIncrement} />
</View>
);
};
export default ParentComponent;


In this example, useImperativeHandle is used in the ChildComponent to expose an increment method via the ref object. This method updates the count state variable in the ChildComponent when called. The ParentComponent then utilizes this exposed method to trigger an increment action from a Button component. This allows for a clean separation of concerns between the parent and child components while providing a way for them to communicate effectively.

6. useLayoutEffect

useLayoutEffect is a hook in React Native that is similar to useEffect, but it fires synchronously after all DOM mutations. It is primarily used for imperative DOM operations or for reading layout properties from the DOM and synchronizing state to them.

Syntax

useLayoutEffect(effect, [deps])


Parameters

  • effect: A function that contains imperative code. This function will run synchronously after all DOM mutations.
  • deps (optional): An array of dependencies. The hook will re-run if any of these dependencies change.


Usage

  1. Performing Imperative DOM Operations
    useLayoutEffect is useful for performing imperative DOM operations or reading layout properties from the DOM.
  2. Synchronizing State with DOM
    You can use useLayoutEffect to synchronize state with the DOM after all mutations have been applied.
  3. Optimizing Performance
    useLayoutEffect should be used sparingly and only when the effect needs to run synchronously after DOM mutations.


Example

import React, { useState, useLayoutEffect } from 'react';
import { Text, View } from 'react-native';
const ComponentWithLayoutEffect = () => {
const [width, setWidth] = useState(0);
// useLayoutEffect runs synchronously after all DOM mutations
useLayoutEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
// Add event listener for window resize
window.addEventListener('resize', handleResize);
// Initial update of width
handleResize();
// Clean up the event listener
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<View>
<Text>Window Width: {width}</Text>
</View>
);
};
export default ComponentWithLayoutEffect;


In this example, useLayoutEffect is used to update the state variable width based on the window.innerWidth after all DOM mutations have been applied. The effect runs synchronously after all DOM mutations, ensuring that the state is synchronized with the current layout of the DOM. This is particularly useful for cases where you need to perform imperative operations that depend on the DOM layout

7. useDebugValue

useDebugValue is a hook in React Native that provides a way to display a label for custom hooks in React DevTools. It is mainly used for debugging purposes to provide additional information about the custom hook's value in the DevTools.

Syntax

useDebugValue(value);


Parameters

value: The value or label to be displayed in React DevTools for the custom hook.

Usage

  1. Labeling Custom Hooks
    useDebugValue is typically used inside custom hooks to provide a label or additional information about the hook's value in React DevTools.
  2. Debugging
    It helps developers understand the behavior of custom hooks by displaying meaningful labels or values in the DevTools.


Example


import { useState, useDebugValue } from 'react';
const useCustomHook = (initialValue) => {
const [value, setValue] = useState(initialValue);
// Display label in React DevTools
useDebugValue(value > 10 ? 'Value is greater than 10' : 'Value is not greater than 10');
return [value, setValue];
};
// Usage of the custom hook
const ComponentUsingCustomHook = () => {
const [state, setState] = useCustomHook(5);
return (
<div>
<p>State value: {state}</p>
<button onClick={() => setState(state + 1)}>Increment</button>
</div>
);
};
export default ComponentUsingCustomHook;


In this example, useDebugValue is used inside the custom hook useCustomHook to provide additional information about the value returned by the hook. Depending on whether the value is greater than 10 or not, a corresponding label is displayed in React DevTools. This helps developers understand the behavior of the custom hook and provides valuable debugging information.

Custom hooks

Custom hooks are JavaScript functions that utilize React hooks internally. They allow you to extract and reuse stateful logic from your components, making your code more modular and easier to maintain. Custom hooks follow the naming convention of starting with "use" to signify that they are hooks.

Usage

    1. State Management Custom hooks can encapsulate stateful logic, such as managing form data, handling authentication states, or tracking UI states.
    2. Side Effects Custom hooks can encapsulate side-effect logic, such as fetching data from an API, subscribing to external events, or managing timers.
    3. Complex Logic Custom hooks can encapsulate complex logic, such as handling pagination, managing global state, or implementing feature toggles.

Custom hooks allow you to extract and reuse logic across your application, making your code more modular, readable, and maintainable. They promote the separation of concerns and help avoid code duplication.

Example

// seCounter custom hook
import { useState } from 'react';
const useCounter = (initialValue = 0) => {
const [count, setCount] = useState(initialValue);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return { count, increment, decrement };
};
export default useCounter;

// Usage of the custom hook
import React from 'react';
import useCounter from './useCounter';
const Counter = () => {
const { count, increment, decrement } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default Counter;


In this example, a custom hook named useCounter is created to encapsulate the logic for managing a counter. The useCounter hook manages the count state variable and provides methods for incrementing and decrementing the count. This logic is then reused in the Counter component, making it more modular and easier to understand. Custom hooks enable code reuse and promote separation of concerns in your React applications.

benefits of react native hooks

Benefits of Using Hooks in React Native

Let's delve into some of the key advantages of using React Native hooks

1. Enhanced Code Reusability

Hooks allow you to reuse stateful logic across different components, eliminating the need to rewrite the same logic in various places. This encourages clean and modular code, making your React Native app more maintainable.

2. Improved Readability

Hooks promote a more linear and straightforward coding style. With class components, you often had to split the code across various lifecycle methods. Hooks, on the other hand, keep related code together in the same function, which enhances code readability.

3. Smaller Component Sizes

Class components could become bulky with the increasing need for lifecycle methods and state management. Hooks offer a way to create smaller, more focused components that are easier to understand and maintain.

4. Easier State Management

Managing the state in functional components has never been more straightforward. With the useState and useContext hooks, you can easily create and manage a component-specific state or access global state.

5. Improved Testing

Hooks make it easier to test components because they separate the concerns of state management and side effects from the rendering logic. This separation simplifies unit testing and leads to more robust and predictable tests.


Some commonly asked question

1. What is the use of hooks in React native?

React Native Hooks provide a way to add state, side effects, and other React features to functional components, allowing for simpler and more concise code compared to class components. They offer benefits such as improved code reusability, better readability, and enhanced performance optimization.

2. What is the difference between react native redux and hooks?

React Native Redux is a state management library for managing global state in React Native applications.

React Native Hooks are a feature in React for managing state and side effects within functional components.

Redux is suitable for managing complex global state across the entire application.

Hooks are used within individual components for managing local state and side effects more concisely and functionally.

3. What are the differences between hooks and class in react native?

Aspect

Hooks

Class Components

Syntax

Functions used in functional components

ES6 classes

State Management

useState hook manages state.

setState method

Lifecycle Methods

useEffect hook replaces lifecycle methods

ComponentDidMount, ComponentDidUpdate, etc.

Code Reusability

Promotes code reuse without higher-order components or render props

Can use higher-order components or render props for code reuse

Performance

Offers better performance optimizations

Might have performance issues due to lifecycle methods and bindings

4. Why hooks instead of lifecycle methods?

    1. Simplicity: Hooks simplify component logic by breaking it into smaller, more manageable pieces.
    2. Code Reusability: Hooks enable the encapsulation of logic into reusable custom hooks, reducing duplication.
    3. Avoidance of Class Syntax: Hooks eliminate the need for class components and associated syntax, making code more concise.
    4. Flexibility: Hooks provide more flexibility in managing state and side effects within functional components.
    5. Performance Optimization: Hooks offer fine-grained control over side effects, leading to better performance optimizations.

Conclusion

In conclusion, React Native Hooks revolutionized the way we build mobile applications with React Native. They offer a simpler and more efficient way to manage states, handle side effects, and organize code within functional components. By leveraging hooks like useState, useEffect, useContext, and others, developers can streamline their development process, resulting in cleaner, more maintainable codebases.

With React Native Hooks, developers can enjoy enhanced code reusability, improved readability, smaller component sizes, easier state management, and better testing capabilities. Whether you're a beginner or an experienced developer, integrating React Native Hooks into your projects can elevate your app development skills and significantly enhance the quality of your React Native applications.

So, if you're looking to make your React Native apps more efficient and easier to build, dive into the world of React Native Hooks and unlock their full potential!

Schedule a call now
Start your offshore web & mobile app team with a free consultation from our solutions engineer.

We respect your privacy, and be assured that your data will not be shared