이번 글에서는 React에서 새롭게 제시한 React 코드 작성에 대한 권장사항 9가지를 보고 그 중 새롭게 알게 된 몇가지 사실에 대해 정리해보려 합니다.
1. 반복문에서 요소의 key를 지정해주는 경우에는 index 사용을 지양하자
key는 list의 요소를 추적하기 위해 사용됩니다. 이때 index는 요소를 고유하게 식별하지 않으므로 사용하는 것이 좋지 않습니다
// 🛑 WRONG
return (
<ul>
{items.map((item, index) => (
<li key={index}>…</li>
))}
</ul>
);
// 🟢 RIGHT, item.id가 안정적인 고유한 식별자라고 가정
return (
<ul>
{items.map((item, index) => (
<li key={item.id}>…</li>
))}
</ul>
);
key를 index로 지정하게 되면 배열이 정렬되거나 배열의 시작 부분에 요소가 추가되는 경우, 해당 인덱스를 나타내는 요소가 동일하더라도 인덱스가 변경되고 이로 인해 불필요한 렌더링이 발생합니다.
또, map 함수에서는 key에 index 함수를 사용하게 되면 콜백함수가 돌 때마다 변수가 선언되어 메모리 낭비가 됩니다.
2. 프로퍼티 변경에 따라 상태를 조정해야 하는 경우 컴포넌트 함수에 직접 상태를 설정하자.
2-1.사전 검토
프로퍼티 변경에 따라 상태를 조정할 때는 우선 렌더링 중에 데이터를 도출할 수 있는지를 확인하세요.
간단한 예를 들어, firstName과 lastName이 있고 이들을 연결하여 fullName을 계산한다고 해보겠습니다.
또한 fullName은 firstName 또는 lastName이 변경될 때마다 업데이트 되야합니다.
먼저 useEffect를 통해 데이터를 도출 하겠습니다.
function Form() {
const [firstName, setFirstName] = useState('Bruno');
const [lastName, setLastName] = useState('Mars');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName\]);
}
React 문서에서는 해당 상황에서 useEffect를 사용하는 것은 불필요한 렌더링을 발생 시킬수 있다고 얘기합니다.
이제 렌더링 과정에서 useEffect 없이 데이터를 도출 해보겠습니다.
function Form() {
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const fullName = firstName + ' ' + lastName;
}
이렇게 위의 코드처럼 props나 상태에서 무언가를 계산할 수 있는 경우에는 상태에 넣지 않고 렌더링 중에 계산하게 되면 코드도 간결해질뿐더러 성능적인 측면에서도 이점을 볼 수 있습니다.
2-2. 본론
사전 검토가 끝났다면 이제는 컴포넌트 함수를 통해서 프로퍼티 변경에 따라 상태를 조정 해줘야합니다.
// 🛑 WRONG
function List({ items }) {
const [selection, setSelection] = useState(null);
useEffect(() => {
setSelection(null);
}, [items]);
}
// 🟢 RIGHT
function List({ items }) {
const [prevItems, setPrevItems] = useState(items);
const [selection, setSelection] = useState(null);
if (items !== prevItems) {
setPrevItems(items);
setSelection(null);
}
}
3. 데이터를 fetching 하는 경우 useEffect 대신 라이브러리를 사용하자.
문서에 따르면 useEffect로 데이터를 fetching 할 경우 미세한 버그가 발생할 수 있고 이를 해결하기 위해서는 많은 양의 보일러 플레이트가 요구된다고 합니다.
이러한 이유 때문에 React 문서에서는 React-Query나 SWR 같은 라이브러리를 제안하고 있습니다.
// 🛑 WRONG
const [items, setItems] = useState();
useEffect(() => {
api.loadItems().then(newItems => setItems(newItems));
}, []);
// 🟢 RIGHT (one library option)
import {useQuery} from '@tanstack/react-query';
const { data: items } = useQuery(['items'], () => api.loadItems());
4. 이벤트 발생에 대한 응답으로 어떠한 액션을 취해야 하는 경우, 이벤트 핸들러에 코드를 작성하자.
이벤트 발생에 대한 응답으로 어떠한 액션을 취해야 하는 경우, useEffect가 아닌 이벤트 핸들러에 코드를 작성해야 합니다.
이렇게 하면 이벤트가 한 번 발생할 때 코드도 한 번만 실행됨을 보장할 수 있습니다.
const [savedData, setSavedData] = useState(null);
const [validationErrors, setValidationErrors] = useState(null);
// 🛑 WRONG
useEffect(() => {
if (savedData) {
setValidationErrors(null);
}
}, [savedData]);
function saveData() {
const response = await api.save(data);
setSavedData(response.data);
}
// 🟢 RIGHT
async function saveData() {
const response = await api.save(data);
setSavedData(response.data);
setValidationErrors(null);
}
참고 문헌
'Web Frontend > React' 카테고리의 다른 글
useEffect와 IIFE (0) | 2024.06.08 |
---|---|
Fragment vs Null (2) | 2023.12.22 |
React Batching 이해하기 (2) | 2023.01.06 |