- React hooks are special function that are introduced in React16.8 that allows you to use state and other react features in functional component without use of class component.
-
It is simple and concise way to manage state, side effects, and other lifecycle methods.
Importance of React Hooks :
React hooks simplify the code , and improve the readability , reusability and overall performance of the application.
Some Important React Hooks :
1.useStateHook
2.useEffectHook
3.useRef Hook
4.useMemo Hook
5.useContext Hook
6.useReducer Hook
7.useLayoutEffect Hook
8.custom React Hooks
1.State :
- Before React 16.8 Class components were the only way to track state and lifecycle on a React component. Function components were considered "state-less".
State In Class Component :
- state is a special object that holds dynamic data. It is initialized in the constructor using “this.state” and updated using the “this.setState” method.
import { Component } from "react";
export default class ClassCounter extends Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
console.log('Class Component Counter',this.state.count);
};
render() {
return (
<div>
<p>Class Component Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
State in Functional Component :
- In functional component state is managed using useState hooks.
- This hooks allowed you to declare the state variable and provide to update them.
- Functional Component are orginally stateless But the introduction of hooks made them capable of manging data.
Syntax :
const [state, setState] = useState(initialValue);
- state: This is the current state value.
- setState: This is a function to update the state.
- initialValue: This is the initial value of the state, which can be a primitive, object, array, or function.
import { useState } from 'react';
export default function FunctionalCounter() {
const [count, setCount] = useState(0);
//let count = 0;
const increment = () => {
setCount(count + 1);
// count+=1;
console.log('Functional Component Counter',count);
};
return (
<div>
<p>Functional Component Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
2.UseEffect :
- The useEffect Hook allows you to perform side effects in your components.
- The useEffect hook in functional components serves the same purpose as lifecycle methods in class components [ componentDidmount , compoonentDidUpdate , componentWillUnmount ].
- It allows you to perform side effects like fetching data ,Timers ,Update directly into the DOM
- This means the effect will only run when count changes. If you update any other state, the effect won't run.
Syntax :
useEffect(() => {
//Runs on the first render
//And any time any dependency value changes
}, [prop, state]);
Example :
import { useState, useEffect } from "react";
export default function FunctionalTimer() {
const [count, setCount] = useState(0);
//Without Using Dependency
// useEffect(() => {
// setTimeout(() => {
// setCount((count) => count + 1);
// }, 1000);
// });
//With Dependency Array
useEffect(() => {
setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
}, [count]);
return <h1>{`Functional Component : I've rendered ${count} times!`}</h1>;
}
3.UseRef :
- It can be used to store a mutable value that does not cause a re-render when updated.
- It can be used to access a DOM element directly.
- If we tried to count how many times our application renders using the ” useState ” Hook, we would be caught in an infinite loop since this Hook itself causes a re-render.
- Since useRef hooks preserve value across various re-renders and do not cause re-renders whenever a value is changed they make the application faster and helps in caching and storing previous values
Syntax :
const ref = useRef(initialValue);
Without using useRef Hook :
import { useState, useEffect } from "react";
export default function RenderCounterWithoutRef() {
const [count, setCount] = useState(0);
const [renderCount, setRenderCount] = useState(0);
useEffect(() => {
setRenderCount((prevCount) => prevCount + 1);
},[renderCount]);
const incrementCounter = () => {
setCount((prev) => prev + 1);
};
return (
<div>
<h1>Render Count (Without useRef): {renderCount}</h1>
<h2>Counter Value: {count}</h2>
<button onClick={incrementCounter}>Increment Counter</button>
</div>
);
}
With Using useRef Hooks :
import { useRef, useState, useEffect } from "react";
export default function RenderCounterWithRef() {
const renderCount = useRef(0);
const [count, setCount] = useState(0);
useEffect(() => {
renderCount.current += 1;
});
const incrementCounter = () => {
setCount((prev) => prev + 1);
};
return (
<div>
<h1>Render Count (With useRef): {renderCount.current}</h1>
<h2>Counter Value: {count}</h2>
<button onClick={incrementCounter}>Increment Counter</button>
</div>
);
}
4.Usememo :
- This hooks is used to get the memoized value and prevent the unnecessary re-render of the function in react components.
- It works on the concept of memoization which refers to caching the output of the function for given argument to save computation time and improves the application performance.
import { useState, useMemo } from "react";
export default function ExpensiveCalculationWithMemo() {
const [count, setCount] = useState(0);
const [increment, setIncrement] = useState(0);
const expensiveCalculation = (num) => {
console.log("Calculating...");
return num * 2;
};
//const result = expensiveCalculation(count);
const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);
return (
<div>
<h2>Memoized Value: {memoizedValue}</h2>
<button onClick={() => setCount((prev) => prev + 1)}>Increment Count</button>
<h2>Increment Value: {increment}</h2>
<button onClick={() => setIncrement((prev) => prev + 1)}>Increment</button>
</div>
);
}
5.Usecontext :
- It is a feature in React that allows you to share data across the component tree without having to pass props manually at every level
- React Context is a way to manage state globally.
- It can be used together with the useState Hook to share state between deeply nested components more easily than with useState alone.
import { useContext } from "react";
import { UserProvider,UserContext } from "./UserContext";
// Parent Component
function ParentComponent() {
const user = {
name: "Sabari",
birthYear: 2002,
mobileNumber: "9876543210",
};
return (
<UserProvider value={user}>
<div>
<h1>Parent Component</h1>
<ChildComponent />
</div>
</UserProvider>
);
}
// Child Component
function ChildComponent() {
const user = useContext(UserContext);
return (
<div>
<h2>Child Component</h2>
<p>Name: {user.name}</p>
<p>Birth Year: {user.birthYear}</p>
<p>Mobile Number: {user.mobileNumber}</p>
</div>
);
}
export default ParentComponent;
6.useReducer :
- The useReducer Hook is the better alternative to the useState hook and is generally more preferred over the useState hook.
- when you have complex state-building logic or when the next state value depends upon its previous value or when the components are needed to be optimized.
Syntax :
const [state, dispatch] = useReducer(reducer, initialState);
- reducer: The function to handle state updates.
- initialState: The starting value of the state.
- state: The current state.
- dispatch: A function to trigger state transitions.
import { useReducer } from "react";
export default function Counter() {
// Reducer function
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
case "reset":
return { count: 0 };
default:
throw new Error("Unknown action type");
}
}
// Initial state
const initialState = { count: 0 };
// useReducer hook
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>Count: {state.count}</h1>
<button onClick={() => dispatch({ type: "increment" })}>Increment</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
</div>
);
}
7.useLayoutEffect :
- The React JS useLayoutEffect works similarly to useEffect but rather works asynchronously like the useEffect hook, it fires synchronously after all DOM loading is done loading.
- This is useful for synchronously re-rendering the DOM and also to read the layout from the DOM.
- useEffect happens after the page is shown to the user.
- useLayoutEffect happens before the page is shown, so it’s great for fixing layout issues or measuring things on the page.
import { useState, useLayoutEffect, useEffect } from "react";
export default function LayoutEffect() {
const [value, setValue] = useState("Initial Value");
// useLayoutEffect: Runs before the screen updates
useLayoutEffect(() => {
console.log("useLayoutEffect is triggered with value:", value);
}, [value]);
// useEffect: Runs after the screen is updated
useEffect(() => {
console.log("useEffect is triggered with value:", value);
}, [value]);
setTimeout(() => {
setValue("Updated Value");
}, 5000);
return (
<div>
<h1 style={{ color: "blue" }}>{value}</h1>
<p>
Check the console to see the order of useLayoutEffect and useEffect.
</p>
</div>
);
}
8.Customhooks :
- Hooks are reusable functions.
- When you have component logic that needs to be used by multiple components, we can extract that logic to a custom Hook.
- Custom Hooks start with "use". Example: useCounter.
import { useState } from "react";
export default function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount((prev) => prev + 1);
const decrement = () => setCount((prev) => prev - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
}
import useCounter from "./useCounter";
export default function CounterCustom() {
const { count, increment, decrement, reset } = useCounter(5);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
}