map을 통한 렌더링
export function Parent() {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
useEffect(() => {
setTimeout(() => {
setArray((prev) => [6,7,8,9,10,...prev]);
}, 3000);
}, []);
return (
<>
{array.map((item) => (
<Child key={item} item={item} />
)}
</>
);
}
export function Child({ item }: { item: number }) {
console.log(item);
return <>{item}</>
}
실험 결과: 1 2 3 4 5
export function Parent() {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
useEffect(() => {
setTimeout(() => {
setArray((prev) => [6,7,8,9,10,...prev]);
}, 3000);
}, []);
return (
<>
{array.map((_, index) => (
<Child key={index} item={item} />
)}
</>
);
}
실험 결과: 6, 7, 8, 9, 10, 1, 2, 3, 4, 5
export function Parent() {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
useEffect(() => {
setTimeout(() => {
setArray((prev) => [...prev.slice(0, 2), 10, ...prev.slice(3,prev.length)]);
}, 3000);
}, []);
return (
<>
{array.map((item) => (
<Child key={item} item={item} />
)}
</>
);
}
export function Child({ item }: { item: number }) {
return <>{item}</>
}
실험 결과: 10
export function Parent() {
const [array, setArray] = useState([1, 2, 3, 4, 5]);
useEffect(() => {
setTimeout(() => {
setArray((prev) => [...prev.slice(0, 2), 10, ...prev.slice(3,prev.length)]);
}, 3000);
}, []);
return (
<>
{array.map((_, index) => (
<Child key={index} item={item} />
)}
</>
);
}
export function Child({ item }: { item: number }) {
return <>{item}</>
}
실험 결과: 10
실험 1
상태: [1, 2, 3, 4, 5] => [6, 7, 8, 9, 10, 1, 2, 3, 4, 5]
결과:
- react.memo로 감싸지 않았을 경우:
- key가 item인 경우: 리렌더링시 6, 7, 8, 9, 10, 1, 2, 3, 4, 5 출력
- key가 index인 경우: 리렌더링시 6, 7, 8, 9, 10, 1, 2, 3, 4, 5 출력
- react.memo로 감쌌을 경우:
- key가 item인 경우: 리렌더링시 6, 7, 8, 9, 10 출력
- key가 index인 경우: 6, 7, 8, 9, 10, 1, 2, 3, 4, 5 출력
실험 2
상태: [1, 2, 3, 4, 5] => [1, 2, 10, 4, 5]
결과:
- react.memo로 감싸지 않았을 경우:
- key가 item인 경우: 리렌더링시 1, 2, 10, 4, 5 출력
- key가 index인 경우: 리렌더링시 1, 2, 10, 4, 5 출력
- react.memo로 감쌌을 경우:
- key가 item인 경우: 리렌더링시 10 출력
- key가 index인 경우: 1, 2, 10, 4, 5 출력
결론:
- react memo를 사용하면 부모 리렌더링시, 동일한 key에 대해 prop이 변경되지 않는다면 자식의 리렌더링이 발생하지 않는다. 다만 prop이 변경되면 리렌더링이 발생한다.
- react memo를 사용하지 않는다면 부모 리렌더링시 항상 자식의 리렌더링이 발생한다.
실험 2
부모 컴포넌트 상태: [1, 2, 3] => [3, 2, 1]
자식 컴포넌트 상태: text
export function Parent() {
const [array, setArray] = useState([1,2,3]);
useEffect(() => {
setTimeout(() => {
setArray((prev) => prev.toReversed());
}, 3000);
});
return (
<>
{array.map((item) => (
<Child key={item} item={item} />
)}
</>
);
}
export function Child({ item }: { item: number }) {
const [text, setText] = useState("");
return (
<>{item}th textfield:
<input value={text} onChange={(event) => setText(event.target.value)}/>
</>
);
}
결과:
실험 2
부모 컴포넌트 상태: [1, 2, 3] => [3, 2, 1]
자식 컴포넌트 상태: text
export function Parent() {
const [array, setArray] = useState([1,2,3]);
useEffect(() => {
setTimeout(() => {
setArray((prev) => prev.toReversed());
}, 3000);
});
return (
<>
{array.map((item) => (
<Child key={item} item={item} />
)}
</>
);
}
export function Child({ item }: { item: number }) {
const [text, setText] = useState("");
return (
<>{item}th textfield:
<input value={text} onChange={(event) => setText(event.target.value)}/>
</>
);
}
export function Parent() {
const [array, setArray] = useState([1,2,3]);
useEffect(() => {
setTimeout(() => {
setArray((prev) => prev.toReversed());
}, 3000);
});
return (
<>
{array.map((item) => (
<Child key={index} item={item} />
)}
</>
);
}
export function Child({ item }: { item: number }) {
const [text, setText] = useState("");
return (
<>{item}th textfield:
<input value={text} onChange={(event) => setText(event.target.value)}/>
</>
);
}
결과:
원인:
- 이전렌더 key의 컴포넌트 종류 === 이후 렌더 컴포넌트 종류일 경우 unmount/mount하지 않는다. 실험 같은 경우 Child로 이전 렌더와 이후 렌더의 컴포넌트 종류 같아서 unmount/mount가 발생하지 않는다.
- 이전 렌더 컴포넌트와 이후 렌더 컴포넌트에서 같은 종류의 컴포넌트 instance에 대해서는, state가 유지된다. 즉, key와 state가 매핑된다. 따라서 index를 key로 사용하였을 경우, array 순서 변경이 일어날 때 index와 자식 상태(text)가 매핑되어서 순서 변경이 제대로 일어나지 않는다. 반면 item을 key로 사용하였을 경우에는 array 순서 변경이 일어날 때 item와 자식 상태(text)가 매핑되어서 순서 변경이 정상적으로 같이 일어난다.
결론
- index를 key로 사용해도 괜찮다. 다만 요소 순서의 변경이 일어날 때는 index를 key로 사용하면 안 된다.
- 자식 컴포넌트의 상태가 key와 매핑되는데, index와 매핑되는 경우 요소 순서 변경이 일어나도 상태 순서 변경이 일어나지 않기 때문이다.
Top comments (0)