10 React Hooks Explained
React provides a bunch of standard in-built hooks:
useState
: To manage states. Returns a stateful value and an updater function to update it.useEffect
: To manage side-effects like API calls, subscriptions, timers, mutations, and more.useContext
: To return the current value for a context.useReducer
: AuseState
alternative to help with complex state management.useCallback
: It returns a memorized version of a callback to help a child component not re-render unnecessarily.useMemo
: It returns a memoized value that helps in performance optimizations.useRef
: It returns a ref object with a.current
property. The ref object is mutable. It is mainly used to access a child component imperatively.useLayoutEffect
: It fires at the end of all DOM mutations. It's best to useuseEffect
as much as possible over this one as theuseLayoutEffect
fires synchronously.useDebugValue
: Helps to display a label in React DevTools for custom hooks.
Before React Hooks (React < 16.8), developer's were required to write class
components in order to take advantage of certain React Features. But now, React Hooks
provides a more ergonomic way to build components because we can use stateful logic without changing our component hierarchy.
There are 10 Hooks in total 🔥
🚀 useState :
It is the most important and often used hook. The purpose of this hook to handle reactive data, any data that changes in the application is called state, when any of the data changes, React re-renders the UI.
const [count, setCount] = React.useState(0);
🚀 useEffect :
It allows us to implement all of the lifecycle hooks from within a single function API.
// this will run when the component mounts and anytime the stateful data changes
React.useEffect(() => {
alert('Hey, Nads here!');
});
// this will run, when the component is first initialized
React.useEffect(() => {
alert('Hey, Nads here!');
}, []);
// this will run only when count state changes
React.useEffect(() => {
fetch('nads').then(() => setLoaded(true));
}, [count]);
// this will run when the component is destroyed or before the component is removed from UI.
React.useEffect(() => {
alert('Hey, Nads here');
return () => alert('Goodbye Component');
});
🚀 useContext :
This hook allows us to work with React's Context API
, which itself a mechanism to allow us to share data within it's component tree without passing through props. It basically removes prop-drilling
const ans = {
right: '✅',
wrong: '❌'
}
const AnsContext = createContext(ans);
function Exam(props) {
return (
// Any child component inside this component can access the value which is sent.
<AnsContext.Provider value={ans.right}>
<RightAns />
</AnsContext.Provider>
)
}
function RightAns() {
// it consumes value from the nearest parent provider.
const ans = React.useContext(AnsContext);
return <p>{ans}</p>
// previously we were required to wrap up inside the AnsContext.Consumer
// but this useContext hook, get rids that.
}
🚀 useRef :
This hook allows us to create a mutable object. It is used, when the value keeps changes like in the case of useState hook, but the difference is, it doesn't trigger a re-render when the value changes.
The common use case of this, is to grab HTML elements from the DOM.
function App() {
const myBtn = React.useRef(null);
const handleBtn = () => myBtn.current.click();
return (
<button ref={myBtn} onChange={handleBtn} >
</button>
)
}
🚀 useReducer :
It does very similiar to setState, It's a different way to manage state using Redux Pattern
. Instead of updating the state directly, we dispatch
actions, that go to a reducer
function, and this function figure out, how to compute the next state.
function reducer(state, dispatch) {
switch(action.type) {
case 'increment':
return state+1;
case 'decrement':
return state-1;
default:
throw new Error();
}
}
function useReducer() {
// state is the state we want to show in the UI.
const [state, dispatch] = React.useReducer(reducer, 0);
return (
<>
Count : {state}
<button onClick={() => dispatch({type:'decrement'})}>-</button>
<button onClick={() => dispatch({type:'increment'})}>+</button>
</>
)
}
🚀 useMemo :
This hook will help you to optimise computational cost or improve performance. It mostly used when we're needed to make expensive calculations.
function useMemo() {
const [count, setCount] = React.useState(60);
const expensiveCount = useMemo(() => {
return count**2;
}, [count]) // recompute when count changes.
}
Works great for memoizing returned values, but in other CSSNamespaceRule
, we want to memoize the whole function, in that case we can use this hook ↓
🚀 useCallback :
function useCallbackDemo() {
const [count, setCount] = useState(60);
const showCount = React.useCallback(() => {
alert(`Count ${count}`);
}, [count])
return <> <SomeChild handler = {showCount} /> </>
}
🚀 useImperativeHandle :
This hook is use to modify the exposed ref and it is rarely used.
function useImperativeHandleDemo(props, ref) {
const myBtn = useRef(null);
React.useImperativeHandle(ref, () => ({
click: () => {
console.log('clicking button!');
myBtn.current.click();
}
}));
}
🚀 useLayoutEffect :
It works same as useEffect hook with one difference, the callback will run after rendering the component but before the actual updates have been painted to the screen.
⚠️ : Blocks visual updates until your callback is finished.
function useLayoutEffectDemo() {
const myBtn = React.useRef(null);
React.useLayoutEffect(() => {
const rect = myBtn.current.getBoundingClientRect();
// scroll position before the dom is visually updated
console.log(rect.height);
})
}
🚀 useDebugValue :
This hook doesn't make much sense, but it allows us to define our own custom labels in React Dev Tools
, which are useful for debugging. Suppose we have n
number of components which uses the same logic, then we can separately define our own function and that can be used in other components, but the key thing here is we can debug things
function useDisplayName() {
const [displayName, setDisplayName] = React.useState();
React.useEffect(() => {
const data = fetchFromDatabase(props.userId);
setDisplayName(data.displayName);
}, []);
React.useDebugValue(displayName ?? 'loading...');
return displayName;
}
The return value can be used in other components or else where in the application like this 👇🏽
function App() {
const displayName = useDisplayName();
return <button>{displayName}</button>;
}
References - Fireship's Youtube Video
- React Hooks