
React에서 useEffect로 무한 반복을 피하는 방법
React 프로그래밍에서 상태를 업데이트할 때 무한 반복을 처리하는 것은 흔한 문제입니다. 이는 useEffect 훅의 의존성이 제대로 지정되지 않을 때 발생할 수 있습니다. 이번 블로그 포스트에서는 상태 업데이트로 인해 무한 반복이 발생하는 시나리오를 살펴보고 이 문제에 대한 해결책을 논의하겠습니다.
문제: ESLint에 의해 유발된 무한 반복
React 애플리케이션에서 할 일 목록을 가지고 있고 사용자가 제목 또는 우선순위 등을 기준으로 목록을 정렬할 수 있도록하려고 합시다. 할 일 목록과 현재 선택된 정렬 키를 나타내는 두 개의 상태 변수 todos와 sortKey를 가지고 있습니다.
const [todos, setTodos] = useState([]);
const [sortKey, setSortKey] = useState('title');
const setSortedTodos = useCallback((data) => {
const cloned = data.slice(0);
const sorted = cloned.sort((a, b) => {
const v1 = a[sortKey].toLowerCase();
const v2 = b[sortKey].toLowerCase();
if (v1 < v2) {
return -1;
}
if (v1 > v2) {
return 1;
}
return 0;
});
setTodos(sorted);
}, [sortKey]);
useEffect(() => {
setSortedTodos(todos);
}, [setSortedTodos]);
위의 코드는 예상대로 동작하는 것처럼 보이지만 ESLint에서 useEffect의 의존성 배열에 todos를 추가하도록 요구합니다. 이 조언에 따르면 무한 반복이 발생합니다.
해결책: useEffect 대신 useMemo 사용하기
무한 반복을 방지하기 위해 useEffect 대신 useMemo 훅을 사용할 수 있습니다. 할 일 목록을 정렬한 배열을 메모이징함으로써 정렬 키 또는 할 일 목록 배열이 변경될 때마다 이 배열을 얻을 수 있습니다.
const sortedTodos = useMemo(() => {
return Array.from(todos).sort((a, b) => {
const v1 = a[sortKey].toLowerCase();
const v2 = b[sortKey].toLowerCase();
if (v1 < v2) {
return -1;
}
if (v1 > v2) {
return 1;
}
return 0;
});
}, [sortKey, todos]);
이제 단순히 컴포넌트의 렌더링 로직에서 sortedTodos 배열을 사용하여 정렬된 할 일 목록을 표시할 수 있습니다. 이 접근 방식은 정렬된 배열이 필요한 경우에만 계산되므로 불필요한 다시 렌더링을 방지하고 무한 반복의 가능성을 피할 수 있습니다.
최종 구현
const { useState, useCallback, useMemo } = React;
const exampleToDos = [
{title: "This", priority: "1 - high", text: "Do this"},
{title: "That", priority: "1 - high", text: "Do that"},
{title: "The Other", priority: "2 - medium", text: "Do the other"},
];
function Example() {
const [sortKey, setSortKey] = useState('title');
const [todos, setTodos] = useState(exampleToDos);
const sortedTodos = useMemo(() => {
return Array.from(todos).sort((a, b) => {
const v1 = a[sortKey].toLowerCase();
const v2 = b[sortKey].toLowerCase();
if (v1 < v2) {
return -1;
}
if (v1 > v2) {
return 1;
}
return 0;
});
}, [sortKey, todos]);
const sortByChange = useCallback((e) => {
setSortKey(e.target.value);
}, []);
return (
<div>
정렬 기준:
<select onChange={sortByChange}>
<option selected={sortKey === "title"} value="title">제목</option>
<option selected={sortKey === "priority"} value="priority">우선순위</option>
</select>
{sortedTodos.map(({ text, title, priority }) => (
<div className="todo">
<h4>{title} <span className="priority">{priority}</span></h4>
<div>{text}</div>
</div>
))}
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("root"));
축하합니다! useEffect 대신 useMemo를 사용하여 React에서 상태를 업데이트하는 무한 반복 문제를 성공적으로 해결했습니다. 이제 성능이나 불필요한 다시 렌더링을 걱정하지 않고 할 일 목록을 올바르게 정렬할 수 있습니다.
상태 및 해당 의존성에서 값을 유도해야 하는 경우 useMemo를 사용하고 데이터를 가져오거나 이벤트에 대해 구독하는 것과 같은 부작용에는 useEffect를 사용하는 것을 기억하세요. 즐거운 코딩하세요!