이 글은 Dev quiz 어플리케이션 null의 퀴즈 해설을 위한 글입니다.
보러가기 -> https://github.com/0hhanum/null_ios
question:
- 당신은 열정적인 개발자입니다.
- 아래 코드리뷰 요청에 대한 당신의 응답은?
===========================================================
const Component = ({ items }) => {
const [filter, setFilter] = useState(null);
const [filteredItems, setFilteredItems] = useState([]);
useEffect(() => {
if (filter) {
setFilteredItems(items.filter({state} => state === filter));
}
}, [filter]);
...
===========================================================
choices:
- "filteredItems 상태도, useEffect도 필요없다."
- "LGTM 👍"
answer:
- A
문제의 코드는 이렇게 정리하는 것이 이해하기도, 성능적으로도 좋습니다.
const Component = ({ items }) => {
const [filter, setFilter] = useState(state.default);
const filteredItems = items.filter(({ state }) => state === filter);
...
};
useEffect
없이도 필터링 된 배열을 만들 수 있습니다.
무슨 차이가 있을까요?
useEffect
는 읽고 이해하기 쉽지 않습니다.
문제의 경우 단순하지만 컴포넌트가 복잡해질수록 코드를 변경할 때 신경 쓸 부분이 많아집니다.
상태 변경에 대응해 어떤 로직을 실행해야 할 경우, 무의식적으로 useEffect를
사용하는 것은 많은 리액트 개발자가 저지르는 실수입니다.
해당 로직이 외부 시스템과 함께 수행되어야 하는 side effect
가 맞는지 고민해봐야 합니다.
일반적으로 비동기 데이터를 요청하거나, 브라우저 API를 이용하거나, 타이머 함수 사용 등이 이에 해당합니다.
위의 경우 그렇지 않죠.
컴포넌트를 이해하기 어렵게 만들 뿐 아니라, filteredItems
라는 상태를 도입해 불필요한 추가 렌더링을 발생시킵니다.
수정한 코드는 filteredItems
를 상태로 두지 않았습니다.
filteredItems
상태를 두는 것은 추가 렌더링 외에도 데이터 동기화 문제가 있습니다.
items
의 데이터를 복사한 새 배열을 상태로 저장하고 있어서 items
가 업데이트 되어도 filteredItems
에 복사한 낡은 데이터를 가지고 있게 됩니다.
이는 특히 filteredItems
의 요소를 이용해 특정 작업을 수행하려고 할 경우 찾기 어려운 버그를 만들 가능성이 있습니다.
수정한 방식은 items
의 변경에 따라 새로운 filteredItems
배열을 만들어 동기화를 따로 할 필요가 없습니다.
이처럼 useEffect
를 사용할 때에는 정말 필요한 부분인 지 확인해보는게 좋겠죠?