Hooks API Reference – React
A JavaScript library for building user interfaces
ko.reactjs.org
본 게시글은 React 공식 문서와 한입 크기로 떠먹는 리액트 강의를 기반으로 작성된 게시글입니다.
useMemo, useCallback는 React에서 최적화 기능을 하는 Hooks입니다.
정리 전 React 공식 문서에 나와있는 useMemo, useCallback에 대한 문서를 확인해보도록 하겠습니다.
React 공식 문서에서 공통적으로 Memoization 이라는 내용을 확인할 수 있는 내용이 있습니다.
Memoization 이란 이미 계산해 본 연산 결과를 기억해 두었다가 동일한 계산을 시키면, 다시 연산하지 않고
기억해두었던 데이터(계산 값)를 반환하게 하는 방법입니다.
예를 들어 설명하면 마치 시험을 볼 때 이미 풀어본 문제는 다시 풀어보지 않아도 답을 알고 있는 것과 유사하며 이런 상황을 Memoization을 이용하여 연산 과정을 최적화했다 라고 할 수 있습니다.
또한 Memoization을 시용했다는 것은 연산을 최적화하기 위해 해당 문제에 대한 답을 기억해두었다는 뜻이 되겠습니다.
즉 Memoization은 기출문제의 답을 외우 듯 답이 같은 상황에서 같은 계산을 시키면 다시 계산하지 않고 기억해 둔 답만 반환하는 것을 의미하는 것입니다.
그럼 Memoization이 React의 어느 부분에 적용되어야하는지 정리해보도록 하겠습니다.
Memoization이 필요한 상황을 아래 코드로 간략하게 확인해보겠습니다.
( 해당 코드는 최적화를 설명을 위한 코드이기 때문에 관련 코드 이외의 코드는 생략하도록 하겠습니다. )
const App = () => {
const [data, setData] = useState([]);
// 삭제
const onRemove = (id) => {
console.log(`${id}번째 일기가 삭제되었습니다.`);
const newDiaryList = data.filter((it) => it.id !== id);
// console.log(newDiaryList);
setData(newDiaryList);
};
// 수정
const onEdit = (id, newContent) => {
setData(
data.map((it) => (it.id === id ? { ...it, content: newContent } : it))
);
};
// emotion 평가 함수 ( 기분이 좋은 일기가 현재 몇개있는지 카운팅 및 비율, 기분이 좋지 않은 일기는 현개 몇개 있는지 카운팅, )
const getDiaryAnalysis = () => {
console.log("일기 분석 시작!");
// 카운팅
const goodCount = data.filter((it) => it.emotion >= 2).length;
const badCount = data.length - goodCount;
// 비율
const goodRatio = (goodCount / data.length) * 100;
return {
goodCount,
badCount,
goodRatio,
};
};
const { goodCount, badCount, goodRatio } = getDiaryAnalysis();
return (
<div className="App">
<DiaryEditor onCreate={onCreate} />
<div>전체 일기 : {data.length}</div>
<div>기분 좋은 일기의 갯수 : {goodCount}</div>
<div>기분 나쁜 일기의 갯수 : {badCount}</div>
<div>기분 좋은 일기의 비율 : {goodRatio} %</div>
<DiaryList diaryList={data} onRemove={onRemove} onEdit={onEdit} />
</div>
);
};
export default App;
( getDiaryAnalysis 함수는 1 ~ 5 중 선택된 값을 기분 좋음과 기분 나쁨으로 분류하고 해당 기분을 카운트하고 비율을 계산하는 함수입니다. )
우선 참고하고 가야할 것이 컴포넌트는 함수형으로 컴포넌트를 만들었지만 결국 JavaScript 함수라는 점이며, 함수가 실행될 때마다 내부에 선언되어 있던 변수나 다른 함수도 매번 다시 선언되어 사용된다는 점입니다.
이점은 컴포넌트의 리렌더링, 즉 해당 컴포넌트가 전달받는 prop의 값이 변경되거나 해당 컴포넌트 안에 선언된 State의 값이 변하게 되면 컴포넌트는 업데이트가 일어나고, App함수가 한번 더 호출되게 됩니다.
그렇기 때문에 App 컴포넌트의 리 렌더링이 발생하면 컴포넌트 내부에 선언된 getDiaryAnalysis 함수를 호출하는 코드인
const { goodCount, badCount, goodRatio } = getDiaryAnalysis(); 도 당연히 재선언됨과 동시에 실행되게 됩니다.
위의 내용을 기반으로 만약 일기의 내용을 수정하게 되면 data State가 다시 한번 변경된 것이기 때문에 App 컴포넌트는 다시 리 렌더링이 발생하게 됩니다.
( data State는 작성자, 내용, 작성 시간 등을 담은 객체입니다. )
이렇게 다시 리 렌더링이 되면 getDiaryAnalysis 함수는 또 호출이 될 것입니다.
( getDiaryAnalysis 함수가 또 호출 되었기 때문에 함수 안에 작성된 콘솔도 다시 찍히게 됩니다. )
정리를 해보면 일기 내용을 수정을 한 상황은 getDiaryAnalysis 함수에 아무런 영향을 미치지 않음에도 불구하고 App 컴포넌트가 리 렌더링 되기 때문에 getDiaryAnalysis 함수는 다시 호출이 되는 매우 비효율적인 작업이 진행이 되게 됩니다.
( getDiaryAnalysis 함수는 일기의 개수가 변경되었을 때만 영향을 받는 함수입니다. 즉 data State의 값인 배열의 길이가 변했을 때만 영향을 받는 함수이며 일기 내용이 수정이되었다는 것은 일기의 개수는 변화가 없다는 것을 의미하기 때문에 일기의 내용을 수정하는 상황에서는 영향을 받지 않는 함수입니다.)
이때 사용할 수 있는 것이 Memoization 기법입니다.
React에서 값을 반환하는 함수에 Memoization 기법을 사용하기 위해서는 useMemo를 사용해주시면 됩니다.
useMemo의 사용법은 Memoization하고 싶은 함수를 감싸주면 됩니다.
결과적으로 보면 getDiaryAnalysis 함수는 useMemo를 호출한 것이 되었으며 useMemo 안에는 인수로 기존의 getDiaryAnalysis 함수가 콜백 함수로 주어진 형태가 되었습니다.
useMemo는 첫 번째 인수로 콜백함수를 받으며 두 번째 인수로 배열을 전달전달해야 합니다.
두 번째 인수로 전달되는 배열은 useEffect에 인수로 전달되는 의존성 배열과 같은 기능을 하는 배열입니다.
두 번째 인수로 전달된 배열에 data.length를 넣게 되면 data.length가 변화할 때만 useMemo에 첫 번째 인수로 전달된 콜백함수가 실행되게 됩니다.
이러한 기능은 앞서 설명한 예시인 기출문제의 답을 외웠다가 답만 체크하는 경우와 같은 맥락으로 생각하면 이해가 쉽게 되실 것입니다.
즉 기존 함수의 연산 과정을 거쳐 나오게 되는 값을 외워 반환되는 값의 변화가 없다면 연산 과정을 거치지 않고 외워둔 값을 반환하게 되는 것입니다.
이렇게 useMemo를 사용하여 최적화를 해주게 되면 App 컴포넌트가 리렌더링 되면서
const { goodCount, badCount, goodRatio } = getDiaryAnalysis(); 의 코드 재실행으로 아무리 getDiaryAnalysis 함수를 호출해도 useMemo의 의존성 배열 안에 있는 값이 변화하지 않으면 함수 안의 연산 과정을 거치지 않고 외워둔 값만 반환하게 됩니다.
여기까지 하고실행하게 되면 아래와 같은 오류가 발생하게 됩니다.
해당 에러가 발생한 이유는 앞서 강조하여 설명한 React에서 값을 반환하는 함수에 Memoization 기법을 사용하기 위해서는 useMemo를 사용에 있습니다.
useMemo를 통해 Memoization을 진행하게 되면 기존의 함수는 더 이상 함수가 아니게 됩니다.
즉 useMemo함수를 통해 반환된 값을 담은 상수(변수)로 바뀌게 됩니다.
때문에 getDiaryAnalysis는 useMemo에서 반환되는 값을 담은 상수이며 코드를 아래와 같이 수정해주어야 합니다.
const { goodCount, badCount, goodRatio } = getDiaryAnalysis;
정리
값을 반환하는 함수가 있는데 반환까지의 연산과정을 최적화하고 싶다면 useMemo를 사용하여 의존성 배열에 어떤 값이 변화할 때만 연산과정을 다시 수행할 것인지 명시해주고 기존의 함수를 값처럼 사용하여 연산 최적화를 할수 있습니다.
다음으로 컴포넌트 재사용을 통한 Memoization 정리해보겠습니다.
우선 컴포넌트를 재사용한다는 것이 어떤 의미인지 살펴보겠습니다.
위의 이미지와 같은 컴포넌트 트리가 있다고 가정해보겠습니다.
App 컴포넌트는 count와 text, 2개의 State를 가지고 있으며 count State는 자식 컴포넌트인 CountView 컴포넌트로, text State는 자식 컴포넌트인 TextView 컴포넌트로 prop으로 각각 전달하고 있습니다.
이때 setCount를 실행시켜 App 컴포넌트의 State를 업데이트해주게 되면, 해당 State를 가진 App 컴포넌트는 리렌더링이 발생하게 되고 CountView 컴포넌트로 전달되는 prop의 값도 바뀌게 될 것입니다.
App 컴포넌트의 State의 값이 변하게 되면 App 컴포넌트는 리렌더링되면서 자식 컴포넌트인 CountView컴포넌트와 TextView 컴포넌트 또한 리렌더링 되게 될 것입니다.
이는 부모 컴포넌트가 리렌더링 되면 자식 컴포넌트 또한 리렌더링 되기 때문이며, 이 과정에서 prop의 변화가 없는 TextView 컴포넌트도 강제로 리렌더링이 일어나게 됩니다.
TextView 컴포넌트는 전달받는 prop의 값이 변화하지 않았기 때문에 리렌더링될 필요가 없음에도 불구하고 부모 컴포넌트가 리렌더링 되었기때문에 TextView 컴포넌트가 리렌더링 되어 성능의 낭비가 발생하게 됩니다.
위와 같이 전달받는 prop의 변화가 없어도 리 렌더링 되는 성능의 낭비를 방지하기 위해 각각의 자식 컴포넌트에 업데이트 조건을 부여할 수 있습니다.
CountView 컴포넌트는 자신이 받는 prop인 count가 바뀔 때만 리렌더링이 발생하도록, TextView 컴포넌트는 자신이 받는 prop인 text가 바뀔 때만 리렌더링이 발생하도록 React.memo을 사용하여 조건을 부여해줄 수 있습니다.
우선 React 공식 문서를 통해 React.memo를 살펴보도록 하겠습니다.
React공식 문서에서 React.memo는 고차 컴포넌트라는단락을 확인할 수 있습니다.
고차 컴포넌트(HOC, Higher Order Component)란 컴포넌트 로직을 재사용하기 위한 React의 고급 기술이며 컴포넌트를 가져와 새로운 컴포넌트를 반환하는 함수를 의미합니다.
React공식 문서 고차 컴포넌트의 예시 코드를 살펴보면 함수를 호출하는데 매개변수로 컴포넌트를 전달했더니 기능이 강화된 컴포넌트를 반환받은 것을 확인할 수 있습니다.
쉽게 말해 고차 컴포넌트라는 것은 함수를 호출해서 매개변수로 컴포넌트를 전달하게 되면 기능이 강화된 컴포넌트를 반환하는 것이라고 할 수 있겠습니다.
다시 React.memo의 공식 문서를 살펴보면 React.memo는 고차 컴포넌트로써 함수로 호출이 되었고, 함수의 매개변수로 컴포넌트를 전달하게 되면 기존보다 강화된 새로운 컴포넌트인 MyComponent로 반환하게 된다 로 이해할 수 있겠습니다.
다음 단락을 간단하게 정리하면 똑같은 prop을 받으면 똑같은 것을 반환으로 정리할 수 있습니다.
즉 부모 컴포넌트에서 자식 컴포넌트로 전달되는 prop의 값이 변화하지 않았음에도 마치 변화한 것처럼 똑같은 prop을 전달해도 prop의 값이 변화하지 않았음을 인지하게 된다면 리렌더링하지 않겠음을 의미하는 것입니다.
정리하면 React.memo라는 고차 컴포넌트이며, 컴포넌트를 감싸주게 되면 prop이 변하지 않으면 리 렌더링 하지 않는 강화된 컴포넌트를 반환받게 되는 것입니다.
( React.memo를 사용해도 해당 컴포넌트 내부에 있는 State가 변화하면 리렌더링이 진행됩니다. )
위의 내용을 예시 코드와함께 살펴보도록 하겠습니다.
import React, { useState } from "react";
export const CountView = ({ count }) => {
return <div>{count}</div>;
};
export const TextView = ({ text }) => {
return <div>{text}</div>;
};
// 부모 컴포넌트
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [text, setText] = useState("");
return (
<div style={{ padding: "50px" }}>
{/* count : 버튼을 클릭하면 기존 값 + 1 */}
<div>
<h2>count</h2>
<CountView count={count} />
<button onClick={() => setCount(count + 1)}>+</button>
</div>
<div>
<h2>text</h2>
<TextView text={text} />
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
</div>
);
};
export default OptimizeTest;
위의 코드를 실행해보면 아래와 같은 결과를 확인할 수 있습니다.
count를 증가시키는 버튼을 클릭했는데 TextView 컴포넌트도 리 렌더링 되거나, text를 입력했는데 CountView 컴포넌트도 리렌더링되는 것을 확인할 수 있습니다.
만약 코드가 길어지고 복잡도가 올라간 컴포넌트가 위와 같은 결과가 나오면 원치 않는성능의 낭비가 발생하게 됩니다.
이때 컴포넌트 재사용 기능을 사용하면 성능의 낭비를 해결할 수 있습니다.
export const CountView = React.memo(({ count }) => {
return <div>{count}</div>;
});
export const TextView = React.memo(({ text }) => {
return <div>{text}</div>;
});
이렇게 기존의 컴포넌트를 위의 코드와 같이 React.memo로 감싸주게 되면 해당 컴포넌트를 전달해주게 되면 해당 컴포넌트가 전달받는 prop의 값이 변화하지않으면 리렌더링 되지 않게됩니다.
또 다른 예시 코드를 살펴보겠습니다.
import React, { useEffect, useState } from "react";
// 자식 컴포넌트
export const CountA = React.memo(({ count }) => {
return <div>{count}</div>;
});
// 자식 컴포넌트
export const CountB = React.memo(({ obj }) => {
return <div>{obj.count}</div>;
});
// 부모 컴포넌트
const OptimizeTest = () => {
const [count, setCount] = useState(1);
const [obj, setObj] = useState({
count: 1,
});
return (
<div style={{ padding: "50px" }}>
{/*
setCount(count)로 업데이트를 했기때문에 State의 상태변화를 일으켰지만
정작 바뀌는 값은 기존의 값입니다.
*/}
<div>
<h2>count A</h2>
<CountA count={count} />
<button
onClick={() => {
setCount(count);
}}
>
A button
</button>
</div>
{/*
setObj({count: obj.count})로 업데이트를 했기때문에 State의 상태변화를 일으켰지만
정작 바뀌는 값은 기존의 값입니다.
*/}
<div>
<h2>Count B</h2>
<CountB obj={obj} />
<button
onClick={() =>
setObj({
count: obj.count,
})
}
>
B button
</button>
</div>
</div>
);
};
export default OptimizeTest;
위의 코드는 각 State 업데이트 함수에 기존 State 값을 전달하여 State의 변화를 일으켰지만 정작 바뀌는 값은 기존의 값이 됩니다.
즉 setCount(count)로 업데이트를 했기 때문에 State의 상태변화를 일으켰지만 정작 바뀌는 값은 기존의 값이 되고
setObj({count: obj.count})로 업데이트를 했기때문에 State의 상태변화를 일으켰지만 정작 바뀌는 값은 기존의 값입니다.
React.memo를 사용했기 때문에 버튼을 클릭했을 때 State 업데이트 함수로 count와 obj State를 업데이트 시켜도 동일한 값으로 업데이트를 시키기 때문에 React.memo는 전달받는 prop의 값이 변화하지 않음을 인지하여 CountA와 CountB 컴포넌트가 리렌더링되지 않는 것을 예상할 수 있습니다.
하지만 button B를 클릭했을 때 CountB 컴포넌트는 예상과 달리 리렌더링되는 것을 확인할 수 있습니다.
결과가 이렇게 나오니 React.memo를 잘 못 사용했다 생각할 수 있지만 React.memo는 정상적으로 작동하고 있습니다.
이런 결과가 나온 이유는 CountB 컴포넌트에 전달된 prop인 obj는 객체이기 때문입니다.
JavaScript에서는 기본적으로 객체를 비교할 때는 얕은 비교를 진행하게 되며 객체는 참조값이기 때문에 객체 안에 같은 값이 들어있다해도 해당 객체가 저장된 메모리 주소는 서로 다릅니다.
이것은 같은 값이 들어 있는 객체를 비교하더라고 값에 대한 비교가 아닌 저장된 메모리공간 주소의 비교를 하게되어 다른 것으로 인식하게 되는 것을 의미합니다.
예시 코드와 함께 살펴보겠습니다.
const a = { count : 1 };
const b = a;
if( a === b ){
console.log( “ 같음 ” );
}else {
console.log( “ 다름 ” );
}
// 결과는 같음 출력
상수 b는 상수 a가 가지고 있는 메모리 공간 주소를 할당받은 것이 되기 때문에 서로 같은 것으로 인식되게 됩니다.
다시 돌아와서 React.memo를 사용했음에도 불구하고 컴포넌트가 리 렌더링 되는 이유는 부모 컴포넌트가 자식 컴포넌트인 CountB 컴포넌트에 prop으로 객체인 obj를 전달할 때 객체 안에 들어있는 값을 전달하는 것이 아니라 객체가 저장된 메모리 공간의 주소를 전달하게 됩니다.
만약 이때 부모 컴포넌트가 리 렌더링 된다면 자식 컴포넌트에 prop으로 전달하는 객체인 obj가 저장된 메모리 공간의 새로운 주소를 전달하게 되기 때문에 자식 컴포넌트인 CountB에 React.memo를 사용했더라도 전달받는 prop으로 매번 메모리 공간의 새로운 주소이기 때문에 리렌더링이 발생하게 되는 것입니다.
이런 경우는 아래와 같은 방법을 사용해주어야 합니다.
리액트 공식문서를 확인해보면 React.memo가 첫 번째 인수 말고도 두 번째 인수를 받는 것을 확인할 수 있습니다.
또한 두 번째 인수로 전달받는 areEqual은 비교 함수로 사용되고 있는 것을 확인할 수 있습니다.
비교 함수에서는 기존의 메모리 공간 주소 값의 비교가 아닌 객체 안에 들어 있는 값의 비교를 진행하게 되고
해당 함수가 true를 반환하면 리렌더링을 하지않고 false를 반환하면 리렌더링을 하게 됩니다.
정리하자면 React.memo가 두 번째 인수로 받는 것은 비교 함수이며 해당 비교 함수가 반환하는 값이 true이면 리 렌더링을 하지 않고, false이면 리렌더링을 하게 되는 것입니다.
// 자식 컴포넌트
// 해당 컴포넌트가 전달 받는 객체를 얕은 비교가 아닌 깊은 비교를 하게하여
// 렌더링 최적화 진행
const CountB = ({ obj }) => {
return <div>{obj.count}</div>;
};
// return되는 값이 true이면 현재 porps와 이전 props가 같음을 의미 : 리렌더링 하지 않음
// return되는 값이 false이면 현재 porps와 이전 props가 다름을 의미 : 리렌더링
const areEqual = (prevProps, nextProps) => {
return prevProps.obj.count === nextProps.obj.count;
};
const MemoizedCountB = React.memo(CountB, areEqual);
CountB 컴포넌트에서 React.memo를 사용하지 않았으며, 비교 함수 areEqual를 만들어 주었습니다.
그리고 새로운 컴포넌트인 MemoizedCountB를 생성하여 React.memo의 첫 번째 인수로 React.memo 사용할 컴포넌트를
두 번째 인수로 비교함수를 전달하였습니다.
이것이 의미하는 것은 고차 함수인 React.memo를 사용하여 첫 번째 인수로 CountB 함수를 전달했으며, 두 번째 인수로 객체 안에 저장된 값을 비교하는 함수를 전달하여 강화된 컴포넌트를 반환받게 되었으며 반환받은 컴포넌트가 MemoizedCountB가 되는 것입니다.
이렇게 생성된 강화된 컴포넌트인 MemoizedCountB를 사용해주었습니다.
이제 객체의 깊은 비교, 즉 객체 안에 들어있는 값의 변화를 인식할 수 있는 MemoizedCountB 컴포넌트를 사용했기 때문에 위와 같은 결과를 확인할 수 있게 되었습니다.
마지막으로 함수를 Memoization 하는 방법을 정리해보겠습니다.
우선 함수도 객체라는 사실을 짚고 넘어가면 좋을 것 같습니다.
부모 컴포넌트에서 자식 컴포넌트로 prop으로 함수를 전달하는 상황을 가정해보겠습니다.
위에서 살펴본 내용처럼 부모 컴포넌트가 리 렌더링 되면 부모 컴포넌트에 기존에 정의되어있던 함수는 재정의될 것이며 함수도 객체이기 때문에 재정의된 함수는 새로운 메모리 공간 주소를 가지게 될 것이고, 자식 컴포넌트는 prop으로 새로운 메모리 공간의 주소를 가진 함수를 전달받게 되기 때문에 리렌더링이 발생하게 됩니다.
이때 수 있는 것이 useCallback입니다.
useCallback의 공식 문서를 먼저 살펴 보겠습니다.
해당 내용에서 가장 중요한 내용은 메모이제이션 된 콜백을 반환한다는 설명입니다.
즉 useCallback은 값을 반환하는 것이 아니라 첫 번째 인수로 전달 받은 메모이제이션 된 콜백함수를 반환하는 기능을 하는 것입니다.
다음으로 중요한 내용은 마지막 단락의 useCallback(fn, deps)은 useMemo(() => fn, deps)와 같다는.
즉 useCallback은 useMemo와 같은 방식으로 작동을 함을 의미하며, 두 번째 인수로 전달 받는 의존성 배열 안의 값이 변화하지 않으면
메모이제이션(기억 해둔) 함수를 재 선언하지 않고 그대로 반환함을 의미합니다.
예시 코드와 함께 살펴보도록 하겠습니다.
목표는 자식 컴포넌트인 DiaryEditor에 전달하는 prop인 onCreate 함수가 재 생성(재 선언)되지 않게 하는 것입니다.
우선 DiaryEditor 컴포넌트에 React.memo를 사용하여 전달 받는 prop이 변경될 때만 리 렌더링 되게 해주었습니다.
그다음 해당 함수는 useCallback 함수의 첫 번째 인수로 전달했으며, 두 번째 인수인 의존성 배열을 빈 배열로 전달하여 첫 Mount시에 함수를 생성하게하고 그 뒤는 해당 함수를 재사용하게 해주었습니다.
여기까지 하고실행해보니 기존의 데이터는 사라지고 onCreate함수에 의해 생성된 데이터만 남아있게 되었습니다.
이렇게 된 이유는 useCallBack을 사용할 때 의존성 배열에 아무 값도 넣지 않았기 때문이며, 부모 컴포넌트가 리렌더링 될 때 함수의 생성을 막았기 때문입니다.
해당 함수는 이전 State의 값을 참조하여 State 업데이트 함수를 실행시키기 때문에 현재의 State를 참조할 수있어야 되기 때문에 항상 최신의 State 값을 가지고 있어야 합니다.
하지만 두 번째 인수로 빈 배열을 전달하여 첫 Mount 시에만 함수를 생성하게 해주었기 때문에 생성 당시의 State 값을 참조하게 되었기 때문에 해당 함수가 참조하는 값은 State에 기본 값으로 전달한 빈 배열이 되기 때문에 위와 같은 결과가 나오게 된 것입니다.
그렇다면 최신의 State를 참조하기 위해 해당 함수가 참조하는 State의 값이 바뀔 때마다 함수를 재생성해주어야 된다는 것인데 위와 같이 의존성 배열에 참조하는 State를 넣어주게 되면 의존성 배열의 값이 변할 때마다 함수는 재생성되기 때문에 Memoization은 실패한 것이 될 것입니다.
이때사용할 수 있는 것이 함수형 업데이트입니다.
State 업데이트 함수에 값을 전달하는 것이 아니라 콜백 함수를 전달하는 것입니다.
State 업데이트 함수의 인수로 콜백 함수를 전달하게 되면 해당 콜백 함수의 매개변수 prevData는 기존의 있던 State를 가져와서 사용하게 되어 최신의 State를 유지할 수 있게 됩니다.
이렇게 함수형 업데이트를 사용해주면 의존성 배열을 비워도 해당 함수는 첫 Mount 될 때만 생성되게 해주어 재사용이 항상 같은 메모리 공간 주소를 가진 재사용 가능한 함수가 될 것이고, 해당 함수가 참조하는 State는 항상 최신의 State를 참조할 수 있게 될 것입니다.
'React' 카테고리의 다른 글
React Routing (0) | 2022.10.18 |
---|---|
useEffect (0) | 2022.10.14 |
useContext (0) | 2022.10.09 |
Hook 사용 규칙 (0) | 2022.10.08 |
useEffect 1 (로그인 / 로그아웃) (0) | 2022.10.03 |