--- title: 'useCallback' author: 'Hun Im' date: 2024-04-02T11:53:04+09:00 category: ['POSTS'] tags: ['Javascript', 'React'] og_image: "/images/gamer.png" keywords: ['Javascript', 'React'] --- Before addressing this question, I was studying the situation where functions enter the dependency array of `useEffect`. Typically, when creating a custom hook, the hook receives state values from the component's props and uses them in the dependency array of `useEffect` for handling operations So, what happens when a function is received as a prop? I contemplated this. **Why Functions Are Included in the Dependency Array** Whenever a React component renders, functions are recreated. In JavaScript, since functions are first-class objects, a function with the same name is treated as a different function each time it is recreated during rendering. Therefore, if a function is included in the dependency array, `useEffect` will run again every time that function is recreated during rendering. **Example Using useCallback** ```js import React, { useState, useEffect, useCallback } from 'react' function SearchComponent() { const [searchQuery, setSearchQuery] = useState('') const [results, setResults] = useState([]) const fetchData = useCallback(() => { fetch(`https://api.example.com/search?q=${searchQuery}`) .then((response) => response.json()) .then((data) => setResults(data.results)) .catch((error) => console.error('Error fetching data:', error)) }, [searchQuery]) // fetchData function depends on searchQuery. useEffect(() => { fetchData() }, [fetchData]) // fetchData is included in the dependency array. return ( <div> <input type="text" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} /> <ul> {results.map((result, index) => ( <li key={index}>{result.name}</li> ))} </ul> </div> ) } export default SearchComponent ``` **Example Without Using useCallback** ```js import React, { useState, useEffect } from 'react' function SearchComponent() { const [searchQuery, setSearchQuery] = useState('') const [results, setResults] = useState([]) useEffect(() => { const fetchData = () => { fetch(`https://api.example.com/search?q=${searchQuery}`) .then((response) => response.json()) .then((data) => setResults(data.results)) .catch((error) => console.error('Error fetching data:', error)) } fetchData() }, [searchQuery]) // useEffect runs every time searchQuery changes. return ( <div> <input type="text" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} /> <ul> {results.map((result, index) => ( <li key={index}>{result.name}</li> ))} </ul> </div> ) } export default SearchComponent ``` **Differences** 1. Function Creation and Memory Usage - When a function is defined directly inside `useEffect`, it is newly defined every time `useEffect` runs due to changes in `searchQuery`. - Because the function is recreated each time, memory usage may increase, and constantly redefining the function can lead to unnecessary computations. - This difference can impact performance-sensitive applications, particularly in components that render frequently. 2. Dependency Management - If the function is not wrapped in `useCallback`, it is not included as a dependency in the dependency array. - `useEffect` will only run when `searchQuery` changes. In this case, even though `fetchData` is newly defined every time `searchQuery` changes, dependency management is not complicated; it simply runs with the new function definition. 3. Preventing Unnecessary Re-renders - When using `useCallback`, the function is only recreated when values in the dependency array change. This helps prevent unnecessary re-renders of the component. - Without `useCallback`, the `fetchData` function is recreated every time, leading to potential unintended re-renders in parts of the component managing other dependencies. **When to Use useCallback** 1. Complex Components - When managing many states: The component becomes complex when managing multiple states that interact with each other. For example, this occurs in form components with many input fields or UIs that dynamically change based on various events. - When there are many child components: The complexity increases when a component renders multiple child components that exchange a lot of data, especially if those child components manage independent states or share the parent component's state. - When there is a lot of conditional rendering: The code can become complex with multiple conditional statements (if, switch, ternary operators) when rendering different UIs based on various conditions. 2. Performance-Sensitive Situations - Frequently rendered components: These components need to respond quickly, such as real-time updating dashboards, chat applications, or UIs with many animations. - Handling large amounts of data: This situation arises when a component needs to process or render a lot of data at once, like rendering thousands of rows in a table or creating complex graphs. - When optimization is needed: If certain operations occur frequently and negatively impact performance, optimization is necessary. This includes using Reactโs optimization hooks like React.memo, useCallback, and useMemo to reduce unnecessary re-renders. In these situations, if the component doesn't operate efficiently, user experience can suffer, making it essential to optimize code or leverage Reactโs performance-related features. **Conclusion** - When Not Using `useCallback`: Defining and executing a function directly inside `useEffect` can lead to the function being recreated every time a value in the dependency array changes, impacting memory and performance. However, in simple situations, this might not be a significant issue. - When Using `useCallback`: It helps prevent unnecessary re-creations of functions, contributing to performance optimization. This method is especially efficient if functions are frequently recreated. Thus, while there may not be a significant difference in small-scale components, using `useCallback` to memoize functions can be a better choice in complex components or performance-sensitive scenarios.
ย
--- title: 'useCallback' author: '์ํ' date: 2024-04-02T11:53:04+09:00 category: ['POSTS'] tags: ['Javascript', 'React'] og_image: "/images/gamer.png" keywords: ['Javascript', 'React'] --- ํด๋น ๊ถ๊ธ์ฆ์ ํด์ ํ๊ธฐ ์ ๋ฐฐ๊ฒฝ์๋, useEffect dependency ๋ฐฐ์ด์ ํจ์๊ฐ ๋ค์ด๊ฐ๋ ์ํฉ์ ๋ํด์ ๊ณต๋ถํ๊ณ ์๋ ์์ ์ด๋ค. ๋ณดํต ์ปค์คํ ํ ์ ๋ง๋ค ๋์๋ ํด๋น ํ ์ปดํฌ๋ํธ์ props์ ์ํ ๊ฐ์ ๋ฐ์์ค๊ณ , ๊ทธ ์ํ ๊ฐ์ useEffect ๋ด๋ถ dependency๋ฐฐ์ด์ ๋ฃ์ด ๋์์ ์ฒ๋ฆฌ ํ๋ค. ๊ทธ๋ ๋ค๋ฉด ํจ์๋ฅผ props๋ก ๋ฐ์์ฌ ๋ ์ด๋ค๊ฐ? ๊ณ ๋ฏผํด๋ณด์๋ค. **ํจ์๊ฐ ์์กด์ฑ ๋ฐฐ์ด์ ๋ค์ด๊ฐ๋ ์ด์ ** ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค ํจ์๋ ๋ค์ ์์ฑ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ์์๋ ํจ์๊ฐ ์ผ๊ธ ๊ฐ์ฒด์ด๋ฏ๋ก, ๋์ผํ ์ด๋ฆ์ ํจ์๋ผ๋ ๋งค ๋ ๋๋ง ์ ์๋กญ๊ฒ ์์ฑ๋ ํจ์๋ ์ด์ ํจ์์ ๋ค๋ฅด๊ฒ ์ทจ๊ธ๋๋ค. ๋ฐ๋ผ์, ์์กด์ฑ ๋ฐฐ์ด์ ํจ์๊ฐ ํฌํจ๋๋ฉด ๊ทธ ํจ์๊ฐ ๋งค ๋ ๋๋ง๋ง๋ค ์ฌ์์ฑ๋ ๋๋ง๋ค useEffect๊ฐ ๋ค์ ์คํ๋๋ค. **useCallback์ ์ฌ์ฉ ํ ๋ ์์ ** ```js import React, { useState, useEffect, useCallback } from 'react' function SearchComponent() { const [searchQuery, setSearchQuery] = useState('') const [results, setResults] = useState([]) const fetchData = useCallback(() => { fetch(`https://api.example.com/search?q=${searchQuery}`) .then((response) => response.json()) .then((data) => setResults(data.results)) .catch((error) => console.error('Error fetching data:', error)) }, [searchQuery]) // fetchData ํจ์๋ searchQuery์ ์์กดํฉ๋๋ค. useEffect(() => { fetchData() }, [fetchData]) // fetchData ํจ์๊ฐ ์์กด์ฑ ๋ฐฐ์ด์ ํฌํจ๋ฉ๋๋ค. return ( <div> <input type="text" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} /> <ul> {results.map((result, index) => ( <li key={index}>{result.name}</li> ))} </ul> </div> ) } export default SearchComponent ``` **useCallback์ ์ฌ์ฉํ์ง ์์ ๋ ์์ ** ```js import React, { useState, useEffect } from 'react' function SearchComponent() { const [searchQuery, setSearchQuery] = useState('') const [results, setResults] = useState([]) useEffect(() => { const fetchData = () => { fetch(`https://api.example.com/search?q=${searchQuery}`) .then((response) => response.json()) .then((data) => setResults(data.results)) .catch((error) => console.error('Error fetching data:', error)) } fetchData() }, [searchQuery]) // searchQuery๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค useEffect๊ฐ ์คํ๋ฉ๋๋ค. return ( <div> <input type="text" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} /> <ul> {results.map((result, index) => ( <li key={index}>{result.name}</li> ))} </ul> </div> ) } export default SearchComponent ``` **์ฐจ์ด์ ** 1. ํจ์ ์์ฑ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ - useEffect ๋ด๋ถ์์ ํจ์๋ฅผ ์ง์ ์ ์ํ ๊ฒฝ์ฐ, ์ด ํจ์๋ searchQuery๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค useEffect์ ํจ๊ป ์๋ก ์ ์๋ฉ๋๋ค. - ๋งค๋ฒ ํจ์๊ฐ ์๋ก ์์ฑ๋๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ด ๋์ด๋ ์ ์๊ณ , ํจ์์ ์ ์ ์์ฒด๊ฐ ๋งค๋ฒ ์๋ก ์์ฑ๋๋ ๊ฒ์ ๋ถํ์ํ ์ฐ์ฐ์ ์ด๋ํ ์ ์์ต๋๋ค. - ์ด ์ฐจ์ด๋ ์ฑ๋ฅ์ ๋ฏผ๊ฐํ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ํฅ์ ๋ฏธ์น ์ ์์ผ๋ฉฐ, ํนํ ์์ฃผ ๋ ๋๋ง๋๋ ์ปดํฌ๋ํธ์์๋ ๋ถ์ ์ ์ธ ์ํฅ์ ์ค ์ ์์ต๋๋ค. 2. ์์กด์ฑ ๊ด๋ฆฌ - useCallback์ ์ฌ์ฉํ์ฌ ํจ์๋ฅผ ๊ฐ์ธ์ง ์์ ๊ฒฝ์ฐ, ์์กด์ฑ ๋ฐฐ์ด์์ ํจ์ ์์ฒด๊ฐ ์์กด์ฑ์ผ๋ก ์ถ๊ฐ๋์ง ์์ต๋๋ค. - useEffect๋ ์ค์ง searchQuery๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ์คํ๋ฉ๋๋ค. ์ด ๊ฒฝ์ฐ searchQuery๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค fetchData ํจ์๊ฐ ์๋ก ์ ์๋์ง๋ง, ์ด๋ก ์ธํด ์์กด์ฑ ๊ด๋ฆฌ๊ฐ ๋ณต์กํด์ง์ง ์์ผ๋ฉฐ, ๋จ์ํ ๋งค๋ฒ ํจ์๊ฐ ์๋ก ์ ์๋๋ ๋ฐฉ์์ผ๋ก ๋์ํ๊ฒ ๋ฉ๋๋ค. 3. ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง - useCallback์ ์ฌ์ฉํ์ฌ ํจ์๋ฅผ ์ ์ํ ๋, ์ด ํจ์๋ ์์กด์ฑ ๋ฐฐ์ด์ ์๋ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง ์๋ก ์์ฑ๋ฉ๋๋ค. ์ด๋ ์ปดํฌ๋ํธ๊ฐ ๋ถํ์ํ๊ฒ ๋ฆฌ๋ ๋๋ง๋๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค. - useCallback์ ์ฌ์ฉํ์ง ์์ผ๋ฉด fetchData ํจ์๊ฐ ๋งค๋ฒ ์๋ก ์์ฑ๋๊ณ , ์ด๋ก ์ธํด ๋ง์ฝ ๋ค๋ฅธ ์์กด์ฑ์ ๊ด๋ฆฌํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด, ์๋์น ์๊ฒ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ ์ ์์ต๋๋ค. **useCallback์ ์ฐ๋ ์ํฉ** 1. ๋ณต์กํ ์ปดํฌ๋ํธ - ์ํ ๊ด๋ฆฌ๊ฐ ๋ง์ ๋: ์ฌ๋ฌ ๊ฐ์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , ๊ฐ ์ํ๊ฐ ์๋ก ๋ณต์กํ๊ฒ ์ํธ์์ฉํ ๋ ์ปดํฌ๋ํธ๊ฐ ๋ณต์กํด์ง๋๋ค. ์๋ฅผ ๋ค์ด, ํผ(form)๊ณผ ๊ฐ์ ์ปดํฌ๋ํธ์์ ๋ง์ ์ ๋ ฅ ํ๋์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ฑฐ๋, ๋ค์ํ ์ด๋ฒคํธ์ ๋ฐ๋ผ UI๊ฐ ๋์ ์ผ๋ก ๋ณํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. - ์์ ์ปดํฌ๋ํธ๊ฐ ๋ง์ ๋: ์ปดํฌ๋ํธ๊ฐ ์ฌ๋ฌ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๊ณ ์ด๋ค ๊ฐ์ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํฉ๋๋ค. ํนํ ์์ ์ปดํฌ๋ํธ๋ค์ด ๊ฐ๊ฐ ๋ ๋ฆฝ์ ์ธ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ฑฐ๋, ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์ํ๋ฅผ ๊ณต์ ํด์ผ ํ๋ ๊ฒฝ์ฐ์ ๋๋ค. - ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ด ๋ง์ ๋: ์ปดํฌ๋ํธ๊ฐ ๋ค์ํ ์กฐ๊ฑด์ ๋ฐ๋ผ ์๋ก ๋ค๋ฅธ UI๋ฅผ ๋ ๋๋งํด์ผ ํ๋ ๊ฒฝ์ฐ, ์ฌ๋ฌ ๊ฐ์ง ์กฐ๊ฑด๋ฌธ(if, switch, ์ผํญ ์ฐ์ฐ์ ๋ฑ)์ด ์ฌ์ฉ๋๋ฉด์ ์ฝ๋๊ฐ ๋ณต์กํด์ง ์ ์์ต๋๋ค. 2. ์ฑ๋ฅ์ด ๋ฏผ๊ฐํ ๊ฒฝ์ฐ - ์์ฃผ ๋ ๋๋ง๋๋ ์ปดํฌ๋ํธ: ํ๋ฉด์ ์์ฃผ ๋ ๋๋ง๋๊ฑฐ๋, ์ฌ์ฉ์๊ฐ ์ธํฐ๋์ ํ ๋๋ง๋ค ๋น ๋ฅด๊ฒ ๋ฐ์ํด์ผ ํ๋ ์ปดํฌ๋ํธ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ์ค์๊ฐ์ผ๋ก ์ ๋ฐ์ดํธ๋๋ ๋์๋ณด๋, ์ฑํ ์ ํ๋ฆฌ์ผ์ด์ , ์ ๋๋ฉ์ด์ ํจ๊ณผ๊ฐ ๋ง์ UI ๋ฑ์ด ์ฌ๊ธฐ์ ํด๋นํฉ๋๋ค. - ๋๋์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋: ์ปดํฌ๋ํธ๊ฐ ํ ๋ฒ์ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ฑฐ๋ ๋ ๋๋งํด์ผ ํ๋ ๊ฒฝ์ฐ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ํ ์ด๋ธ์ ์์ฒ ๊ฐ์ ํ์ ๋ ๋๋งํด์ผ ํ๋ ๊ฒฝ์ฐ๋, ๋ณต์กํ ๊ทธ๋ํ๋ฅผ ๊ทธ๋ ค์ผ ํ๋ ์ํฉ์ด ์ด์ ํด๋นํฉ๋๋ค. - ์ต์ ํ๊ฐ ํ์ํ ๊ฒฝ์ฐ: ํน์ ์์ ์ด ์์ฃผ ๋ฐ์ํ๋ฉด์ ์ฑ๋ฅ์ ๋ถ์ ์ ์ธ ์ํฅ์ ๋ฏธ์น๋ ๊ฒฝ์ฐ ์ต์ ํ๊ฐ ํ์ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ์ค์ด๊ธฐ ์ํด React.memo, useCallback, useMemo์ ๊ฐ์ ๋ฆฌ์กํธ์ ์ต์ ํ ํ ์ ์ฌ์ฉํด์ผ ํ ๋๊ฐ ์์ต๋๋ค. ์ด๋ฌํ ์ํฉ์์๋ ์ปดํฌ๋ํธ๊ฐ ํจ์จ์ ์ผ๋ก ๋์ํ์ง ์์ผ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ์ ํ๋ ์ ์์ผ๋ฏ๋ก, ์ฝ๋์ ์ต์ ํ๋ ๋ฆฌ์กํธ์ ์ฑ๋ฅ ๊ด๋ จ ๊ธฐ๋ฅ๋ค์ ํ์ฉํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. **๊ฒฐ๋ก ** - useCallback์ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ: useEffect ๋ด๋ถ์์ ์ง์ ํจ์๋ฅผ ์ ์ํ๊ณ ์คํํ๋ฉด, ํด๋น ํจ์๋ ์์กด์ฑ ๋ฐฐ์ด์ ํฌํจ๋ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋ก ์์ฑ๋๋ฉฐ ๋ฉ๋ชจ๋ฆฌ์ ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค. ํ์ง๋ง ๊ฐ๋จํ ์ํฉ์์๋ ๋ณ๋ค๋ฅธ ๋ฌธ์ ๊ฐ ์์ ์ ์์ต๋๋ค. - useCallback์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ: ํจ์๊ฐ ๋ถํ์ํ๊ฒ ์ฌ์์ฑ๋๋ ๊ฒ์ ๋ฐฉ์งํ์ฌ ์ฑ๋ฅ ์ต์ ํ์ ๊ธฐ์ฌํ ์ ์์ต๋๋ค. ํนํ, ํจ์๊ฐ ์์ฃผ ์ฌ์์ฑ๋๋ ๊ฒฝ์ฐ ์ด ๋ฐฉ๋ฒ์ด ๋ ํจ์จ์ ์ผ ์ ์์ต๋๋ค. ๋ฐ๋ผ์, ์์ ๊ท๋ชจ์ ์ปดํฌ๋ํธ์์๋ ํฐ ์ฐจ์ด๊ฐ ์์ ์ ์์ง๋ง, ๋ณต์กํ ์ปดํฌ๋ํธ๋ ์ฑ๋ฅ์ ๋ฏผ๊ฐํ ๊ฒฝ์ฐ๋ผ๋ฉด useCallback์ ์ฌ์ฉํ์ฌ ํจ์๋ฅผ ๋ฉ๋ชจ์ด์ ์ด์ (memoization)ํ๋ ๊ฒ์ด ๋ ๋์ ์ ํ์ด ๋ ์ ์์ต๋๋ค.