Sitemap
Pandiyan Mani

Pandiyan Mani

Knowledge is everything.Check out my app at https://play.google.com/store/apps/details?id=com.tutorspot.com

Hooks in React Native

8 min readJul 2, 2024

Lets see what are the hooks that we have in react native and what are their usage.

what is Hooks?

In React Native, Hooks are functions that let you “hook into” React state and lifecycle features from functional components

Hooks was introduced in React 16.8 previously hooks are supported only in class component . But now we can use hooks in our function component too.

Lets see what is Functional Components vs Class Components:

Functional Component:

  1. A functional component is just a plain JavaScript pure function that accepts props as an argument and returns a React element.
  2. Functional components run from top to bottom and once the function is returned it can’t be kept alive.
  3. Also known as Stateless components as they simply accept data and display them in some form, they are mainly responsible for rendering UI.
  4. React lifecycle methods (for example, componentDidMount) cannot be used in functional components.

Example of Functional Component:

import {Text,View} from 'react-native';
export default TextSample = ({name}) => {
return (
<View>
<Text>{name}</Text>
</View>

);

};

Class Components:

In React Native, a class component is an older way to define components before the introduction of functional components with hooks. Class components are more complex and require more boilerplate code compared to functional components. However, they are still widely used and important to understand, especially when maintaining older codebases.

  1. A class component requires you to extend from React Component and create a render() function that returns a React element.
  2. The class component is instantiated and different life cycle method is kept alive and is run and invoked depending on the phase of the class component
  3. Also known as Stateful components because they implement logic and state.
  4. React lifecycle methods can be used inside class components (for example, componentDidMount).
import React, { Component } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

class TextSample extends Component {
// Step 1: Define the state in the constructor
constructor(props) {
super(props);
this.state = {
count: 0,
};
}

// Step 2: Define methods to update the state
increment = () => {
this.setState({ count: this.state.count + 1 });
};

decrement = () => {
this.setState({ count: this.state.count - 1 });
};

// Step 3: Render method to display the UI
render() {
return (
<View style={styles.container}>
<Text style={styles.countText}>Count: {this.state.count}</Text>

{/* Button to increment the count */}
<Button title="Increment" onPress={this.increment} />

{/* Button to decrement the count */}
<Button title="Decrement" onPress={this.decrement} style={styles.button} />
</View>
);
}
}

// Styles for the component
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
countText: {
fontSize: 30,
marginBottom: 20,
},
button: {
marginTop: 10,
},
});

export default TextSample;

Why do we need Hooks?

Hooks allow function components to have access to state and other React features

Lets see the types of Hooks One by One below:

useState:

The React useState Hook allows us to track state in a function component and State generally refers to data.

Call useState to declare a state variable and its updater function. It takes an initial state as an argument and returns an array with two elements:

counter is the the current state value.

setCounter is a function to update the state.

import { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";

const HomeScreen = () => {

const [counter, setCounter] = useState(0)

const updateCountDown = () => {
setCounter(1)
}

return (
<View>
<TouchableOpacity onPress={updateCountDown}>
<Text>Display Count Data: {counter}</Text>
</TouchableOpacity>
</View>
)
}

export default HomeScreen;

So in the above example i have an useState counter where we get the state and setCounter for updating the state.

Inside the TouchableOpacity on Press i am updating the setCounter value to 1 and it automatically render the component where counter has been called.

By default it will be 0 on tap it will changed to 1.

useState with Previous State:

Get Pandiyan Mani’s stories in your inbox

Join Medium for free to get updates from this writer.

we will be getting the old data along with that we will be adding the new item.

When the state is an object, you can use the spread operator(…) to copy the existing properties and then overwrite specific fields.

import { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";

const HomeScreen = () => {

const [counter, setCounter] = useState([{ id: 1, name: "Pandiyan" }])

const updateCounterArray = () => {
setCounter( // Replace the state
[ // with a new array
...counter, // that contains all the old items
{ id: 2, name: "Sai Kumaran" } // and one new item at the end
]
);
}

return (
<View>
<TouchableOpacity onPress={updateCounterArray}>
<Text>Size of Counter Data: {counter.length}</Text>
</TouchableOpacity>
</View>
)
}

export default HomeScreen;

useState with Objects:

When the state is an object, you can use the spread operator to copy the existing properties and then overwrite specific fields.

import { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";

const HomeScreen = () => {

const [result, setResult] = useState({ firstnName: 'Pandiyan', lastName: '' })

const updateFirstName = () => {
setResult({...result,lastName: 'Mani'})
}

return (
<View>
<TouchableOpacity onPress={updateFirstName}>
<Text>FirstName: {result.firstnName}</Text>
<Text>LastName: {result.lastName}</Text>
</TouchableOpacity>
</View>
)
}

export default HomeScreen;

useEffect:

The useEffect hook in React Native is used to handle side effects in functional components, such as fetching data, subscribing to events. It runs after the component renders and can be configured to run only when specific dependencies change.

import { useEffect, useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";

const HomeScreen = () => {

const [result, setResult] = useState({ firstnName: 'Pand', lastName: '' })

const updateFirstName = () => {
setResult({...result,lastName: 'mani'})
}
useEffect(()=> {
console.log("Hi")
},[]) []===> array of dependencies is passed to avoid recalling of useEffect

return (
<View>
<TouchableOpacity onPress={updateFirstName}>
<Text>FirstName: {result.firstnName}</Text>
<Text>LastName: {result.lastName}</Text>
</TouchableOpacity>
</View>
)
}

export default HomeScreen;

each time the console inside useEffect with be called when the component render each time. To avoid that you can pass array of dependencies [].

Empty Dependency Array ([]): The effect runs once when the component mounts.

No Dependency Array: The effect runs after every render.

With Dependencies: The effect runs only when specified dependencies change.

import { useEffect, useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";

const HomeScreen = () => {

const [result, setResult] = useState({ firstnName: 'Pand', lastName: '' })
const [counter,setCounter] = useState(0)

const updateFirstName = () => {
setResult({...result,lastName: 'mani'})
setCounter(1)
}
useEffect(()=> {
console.log("Hi")
},[counter])

return (
<View>
<TouchableOpacity onPress={updateFirstName}>
<Text>FirstName: {result.firstnName}</Text>
<Text>LastName: {result.lastName}</Text>
</TouchableOpacity>
</View>
)
}

export default HomeScreen;

if i pass counter dependencies, then whenever there is change on counter the use effect will run.

useEffect with Cleanup:

Return a cleanup function from useEffect to clean up subscriptions, timers, or other resources.

import { useEffect, useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import ProfileScreen from "./ProfileScreen";

const HomeScreen = () => {

const [counter,setCounter] = useState(true)


const updateFirstName = () => {
setCounter(!counter)

}

return (
<View>
<TouchableOpacity onPress={updateFirstName}>
{counter ? <ProfileScreen/> : null}
</TouchableOpacity>
</View>
)
}

export default HomeScreen;
import { useEffect, useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";

const ProfileScreen= () => {

useEffect(()=> {
const timerId= setInterval(()=>{
console.log("running...")
},2000)

return () => {
clearInterval(timerId)
}

},[])

return (
<View>
<Text>Hi</Text>
</View>
)
}

export default ProfileScreen;

useRef:

The useRef Hook allows you to persist values between renders.

It can be used to store a mutable value that does not cause a re-render when updated.

Initial Value: The value passed to useRef becomes the initial .current value of the reference.

Access: Use myRef.current to read or update the reference.

import React, { useRef, useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const App = () => {
const countRef = useRef(0); // Initialize a ref
const [renderCount, setRenderCount] = useState(0);

const incrementRef = () => {
countRef.current += 1; // Update the ref value
console.log('CountRef:', countRef.current);
};

const triggerRender = () => {
setRenderCount(renderCount + 1); // Trigger re-render
};

return (
<View style={styles.container}>
<Text>CountRef (Mutable): {countRef.current}</Text>
<Text>Render Count (State): {renderCount}</Text>
<Button title="Increment Ref" onPress={incrementRef} />
<Button title="Trigger Render" onPress={triggerRender} />
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});

export default App;

useContext:

The useContext hook in React Native (and React) allows you to access values from a context directly within functional components, without needing to pass props down manually through multiple levels of components.

It’s commonly used for global state management

Problem:

import { useState } from "react";
import ReactDOM from "react-dom/client";

function Component1() {
const [user, setUser] = useState("Jesse Hall");

return (
<>
<h1>{`Hello ${user}!`}</h1>
<Component2 user={user} />
</>
);
}

function Component2({ user }) {
return (
<>
<h1>Component 2</h1>
<Component3 user={user} />
</>
);
}

function Component3({ user }) {
return (
<>
<h1>Component 3</h1>
<Component4 user={user} />
</>
);
}

function Component4({ user }) {
return (
<>
<h1>Component 4</h1>
<Component5 user={user} />
</>
);
}

function Component5({ user }) {
return (
<>
<h1>Component 5</h1>
<h2>{`Hello ${user} again!`}</h2>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component1 />);
Solution:


import { useState, createContext, useContext } from "react";
import ReactDOM from "react-dom/client";

const UserContext = createContext();

function Component1() {
const [user, setUser] = useState("Jesse Hall");

return (
<UserContext.Provider value={user}>
<h1>{`Hello ${user}!`}</h1>
<Component2 />
</UserContext.Provider>
);
}

function Component2() {
return (
<>
<h1>Component 2</h1>
<Component3 />
</>
);
}

function Component3() {
return (
<>
<h1>Component 3</h1>
<Component4 />
</>
);
}

function Component4() {
return (
<>
<h1>Component 4</h1>
<Component5 />
</>
);
}

function Component5() {
const user = useContext(UserContext);

return (
<>
<h1>Component 5</h1>
<h2>{`Hello ${user} again!`}</h2>
</>
);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component1 />);

Example:

import React, { createContext, useContext, useState } from 'react';
import { View, Button, StyleSheet } from 'react-native';

const ThemeContext = createContext();

export default App = () => {
const [theme, setTheme] = useState('dark');

return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<ChildComponent />
</ThemeContext.Provider>
);
};

const ChildComponent = () => {
const { theme, setTheme } = useContext(ThemeContext);

return (
<View style={{ backgroundColor: theme === 'light' ? '#fff' : '#333', flex: 1 }}>
<Button
title="Toggle Theme"
onPress={() => setTheme(theme === 'light' ? 'dark' : 'light')}
/>
</View>
);
};

useMemo

The useMemo hook is used to optimize performance by memoizing the result of a computation. It ensures that expensive or computationally intensive functions are only re-executed when their dependencies change, avoiding unnecessary recalculations.

import React, { useState, useMemo } from 'react';
import { View, Text, Button } from 'react-native';

const App = () => {
const [count, setCount] = useState(0);
const [anotherState, setAnotherState] = useState(false);

// Expensive calculation
const expensiveCalculation = useMemo(() => {
console.log('Calculating...');
return count * 2;
}, [count]);

return (
<View>
<Text>Count: {count}</Text>
<Text>Double Count (Expensive): {expensiveCalculation}</Text>
<Button title="Increment Count" onPress={() => setCount(count + 1)} />
<Button title="Toggle State" onPress={() => setAnotherState(!anotherState)} />
</View>
);
};

export default App;

useReducer

The useReducer hook in React Native is a state management hook that's particularly useful when managing complex state logic or when the next state depends on the previous state

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

reducer: A function that takes the current state and an action, and returns the new state.

initialState: The initial state value.

import React, { useReducer } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

// Reducer function to manage state changes
const counterReducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
};

const App = () => {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });

return (
<View style={styles.container}>
<Text style={styles.text}>Count: {state.count}</Text>
<Button title="Increment" onPress={() => dispatch({ type: 'increment' })} />
<Button title="Decrement" onPress={() => dispatch({ type: 'decrement' })} />
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 24,
marginBottom: 20,
},
});

export default App;

The state parameter represents the current state.

The action parameter represents an object that usually contains a type property and any additional data required for the update.

The useReducer hook is used to manage the count state.

The counterReducer function defines the actions (increment and decrement) that modify the state.

--

--