Ultimate React Hooks Guide

Ultimate React Hooks Guide

Source: Dev.to

1. useState ## Explanation ## 2. useEffect ## useEffect With Dependencies ## useEffect With an Empty Dependency Array [] ## useEffect Without Dependencies ## Cleanup Function in useEffect ## 3. useContext ## Explanation ## 4. useReducer ## Explanation ## 5. useCallback ## Explanation ## 6. useMemo ## Explanation ## 7. useRef ## Explanation ## 8. useImperativeHandle ## Explanation ## 9. useLayoutEffect ## Explanation ## 10. useDebugValue ## Explanation ## 11. Custom Hooks ## Explanation ## Example: useFetch ## Usage of Custom Hook React Hooks are functions that let you "hook into" React state and lifecycle features from function components. They were introduced in React 16.8. Below is a list of common React hooks, their explanations, and examples of usage. Additionally, this guide covers how to create custom hooks. The useState hook lets you add state to function components. useState returns an array with two elements: the current state value and a function to update it. The useEffect hook lets you perform side effects in function components. The useContext hook lets you access the value of a context. useContext accepts a context object (the value returned from React.createContext) and returns the current context value. The useReducer hook is an alternative to useState for managing complex state logic. useReducer returns an array with the current state and a dispatch function to trigger state updates. The useCallback hook returns a memoized callback. useCallback returns a memoized version of the callback that only changes if one of the dependencies has changed. The useMemo hook returns a memoized value. useMemo only recomputes the memoized value when one of the dependencies has changed. The useRef hook returns a mutable ref object. useRef is useful for accessing DOM elements or persisting values across renders. The useImperativeHandle hook customizes the instance value that is exposed when using ref. useImperativeHandle should be used with React.forwardRef. The useLayoutEffect hook runs after all DOM mutations. useLayoutEffect is similar to useEffect, but it fires synchronously after all DOM mutations. The useDebugValue hook is used to display a label for custom hooks in React DevTools. useDebugValue can be helpful for debugging custom hooks. Custom hooks let you extract component logic into reusable functions. Custom hooks are normal JavaScript functions that can call other hooks. This guide provides a comprehensive overview of React hooks, their usage, and how to create custom hooks. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK: import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } COMMAND_BLOCK: import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } COMMAND_BLOCK: import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } COMMAND_BLOCK: import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } COMMAND_BLOCK: useEffect(() => { console.log('This runs only once after the first render'); }, []); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: useEffect(() => { console.log('This runs only once after the first render'); }, []); COMMAND_BLOCK: useEffect(() => { console.log('This runs only once after the first render'); }, []); COMMAND_BLOCK: useEffect(() => { console.log('This runs after every render'); }); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: useEffect(() => { console.log('This runs after every render'); }); COMMAND_BLOCK: useEffect(() => { console.log('This runs after every render'); }); COMMAND_BLOCK: useEffect(() => { const interval = setInterval(() => { console.log('Interval running...'); }, 1000); return () => { clearInterval(interval); // Cleanup on unmount }; }, []); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: useEffect(() => { const interval = setInterval(() => { console.log('Interval running...'); }, 1000); return () => { clearInterval(interval); // Cleanup on unmount }; }, []); COMMAND_BLOCK: useEffect(() => { const interval = setInterval(() => { console.log('Interval running...'); }, 1000); return () => { clearInterval(interval); // Cleanup on unmount }; }, []); CODE_BLOCK: import React, { useContext } from 'react'; const MyContext = React.createContext(); function MyComponent() { const value = useContext(MyContext); return <div>{value}</div>; } function App() { return ( <MyContext.Provider value="Hello, World!"> <MyComponent /> </MyContext.Provider> ); } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: import React, { useContext } from 'react'; const MyContext = React.createContext(); function MyComponent() { const value = useContext(MyContext); return <div>{value}</div>; } function App() { return ( <MyContext.Provider value="Hello, World!"> <MyComponent /> </MyContext.Provider> ); } CODE_BLOCK: import React, { useContext } from 'react'; const MyContext = React.createContext(); function MyComponent() { const value = useContext(MyContext); return <div>{value}</div>; } function App() { return ( <MyContext.Provider value="Hello, World!"> <MyComponent /> </MyContext.Provider> ); } COMMAND_BLOCK: import React, { useReducer } from 'react'; const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}> Increment </button> <button onClick={() => dispatch({ type: 'decrement' })}> Decrement </button> </div> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useReducer } from 'react'; const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}> Increment </button> <button onClick={() => dispatch({ type: 'decrement' })}> Decrement </button> </div> ); } COMMAND_BLOCK: import React, { useReducer } from 'react'; const initialState = { count: 0 }; function reducer(state, action) { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}> Increment </button> <button onClick={() => dispatch({ type: 'decrement' })}> Decrement </button> </div> ); } COMMAND_BLOCK: import React, { useState, useCallback } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount((prevCount) => prevCount + 1); }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={increment}> Click me </button> </div> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useState, useCallback } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount((prevCount) => prevCount + 1); }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={increment}> Click me </button> </div> ); } COMMAND_BLOCK: import React, { useState, useCallback } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount((prevCount) => prevCount + 1); }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={increment}> Click me </button> </div> ); } COMMAND_BLOCK: import React, { useState, useMemo } from 'react'; function ExpensiveCalculationComponent() { const [count, setCount] = useState(0); const [value, setValue] = useState(''); const expensiveCalculation = useMemo(() => { // Assume this is an expensive calculation return count * 2; }, [count]); return ( <div> <p>Expensive Calculation: {expensiveCalculation}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <input value={value} onChange={(e) => setValue(e.target.value)} /> </div> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useState, useMemo } from 'react'; function ExpensiveCalculationComponent() { const [count, setCount] = useState(0); const [value, setValue] = useState(''); const expensiveCalculation = useMemo(() => { // Assume this is an expensive calculation return count * 2; }, [count]); return ( <div> <p>Expensive Calculation: {expensiveCalculation}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <input value={value} onChange={(e) => setValue(e.target.value)} /> </div> ); } COMMAND_BLOCK: import React, { useState, useMemo } from 'react'; function ExpensiveCalculationComponent() { const [count, setCount] = useState(0); const [value, setValue] = useState(''); const expensiveCalculation = useMemo(() => { // Assume this is an expensive calculation return count * 2; }, [count]); return ( <div> <p>Expensive Calculation: {expensiveCalculation}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <input value={value} onChange={(e) => setValue(e.target.value)} /> </div> ); } COMMAND_BLOCK: import React, { useRef } from 'react'; function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <div> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </div> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useRef } from 'react'; function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <div> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </div> ); } COMMAND_BLOCK: import React, { useRef } from 'react'; function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { inputEl.current.focus(); }; return ( <div> <input ref={inputEl} type="text" /> <button onClick={onButtonClick}>Focus the input</button> </div> ); } COMMAND_BLOCK: import React, { useRef, useImperativeHandle, forwardRef } from 'react'; const FancyInput = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} />; }); function Parent() { const inputRef = useRef(); return ( <div> <FancyInput ref={inputRef} /> <button onClick={() => inputRef.current.focus()}>Focus Input</button> </div> ); } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useRef, useImperativeHandle, forwardRef } from 'react'; const FancyInput = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} />; }); function Parent() { const inputRef = useRef(); return ( <div> <FancyInput ref={inputRef} /> <button onClick={() => inputRef.current.focus()}>Focus Input</button> </div> ); } COMMAND_BLOCK: import React, { useRef, useImperativeHandle, forwardRef } from 'react'; const FancyInput = forwardRef((props, ref) => { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} />; }); function Parent() { const inputRef = useRef(); return ( <div> <FancyInput ref={inputRef} /> <button onClick={() => inputRef.current.focus()}>Focus Input</button> </div> ); } COMMAND_BLOCK: import React, { useLayoutEffect, useRef } from 'react'; function LayoutEffectComponent() { const divRef = useRef(); useLayoutEffect(() => { console.log(divRef.current.getBoundingClientRect()); }); return <div ref={divRef}>Hello, world!</div>; } Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import React, { useLayoutEffect, useRef } from 'react'; function LayoutEffectComponent() { const divRef = useRef(); useLayoutEffect(() => { console.log(divRef.current.getBoundingClientRect()); }); return <div ref={divRef}>Hello, world!</div>; } COMMAND_BLOCK: import React, { useLayoutEffect, useRef } from 'react'; function LayoutEffectComponent() { const divRef = useRef(); useLayoutEffect(() => { console.log(divRef.current.getBoundingClientRect()); }); return <div ref={divRef}>Hello, world!</div>; } CODE_BLOCK: import React, { useState, useDebugValue } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); useDebugValue(isOnline ? 'Online' : 'Offline'); // Imagine this is an actual implementation return isOnline; } Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: import React, { useState, useDebugValue } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); useDebugValue(isOnline ? 'Online' : 'Offline'); // Imagine this is an actual implementation return isOnline; } CODE_BLOCK: import React, { useState, useDebugValue } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); useDebugValue(isOnline ? 'Online' : 'Offline'); // Imagine this is an actual implementation return isOnline; } COMMAND_BLOCK: import { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch(url) .then((response) => response.json()) .then((data) => { setData(data); setLoading(false); }); }, [url]); return { data, loading }; } export default useFetch; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK: import { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch(url) .then((response) => response.json()) .then((data) => { setData(data); setLoading(false); }); }, [url]); return { data, loading }; } export default useFetch; COMMAND_BLOCK: import { useState, useEffect } from 'react'; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch(url) .then((response) => response.json()) .then((data) => { setData(data); setLoading(false); }); }, [url]); return { data, loading }; } export default useFetch; CODE_BLOCK: import React from 'react'; import useFetch from './useFetch'; function App() { const { data, loading } = useFetch('https://api.example.com/data'); if (loading) { return <div>Loading...</div>; } return ( <div> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default App; Enter fullscreen mode Exit fullscreen mode CODE_BLOCK: import React from 'react'; import useFetch from './useFetch'; function App() { const { data, loading } = useFetch('https://api.example.com/data'); if (loading) { return <div>Loading...</div>; } return ( <div> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default App; CODE_BLOCK: import React from 'react'; import useFetch from './useFetch'; function App() { const { data, loading } = useFetch('https://api.example.com/data'); if (loading) { return <div>Loading...</div>; } return ( <div> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default App; - useEffect runs after the first render and after every update. You can also specify dependencies to control when the effect runs. - Runs only once after the initial render (similar to componentDidMount). - Use case: Fetching data when the component mounts, setting up event listeners, etc. - Runs on every render (initial and subsequent re-renders). - Use case: Running code that doesn’t depend on specific state changes, like logging or animations. - If useEffect returns a function, it acts as a cleanup that runs before the next effect or when the component unmounts. - Use case: Cleaning up event listeners, canceling API calls, clearing intervals, etc.