일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 선언
- 리액트
- object
- block
- Absolute
- 프론트엔드스쿨
- CSS
- false
- 선택자
- function
- True
- boolean
- Project
- javascript
- terminal
- Inline
- LIKELION
- display
- js
- array
- http
- API
- position
- STYLE
- 조건문
- react
- ubuntu
- HTML
- 변수
- 멋쟁이사자처럼
- Today
- Total
Jeden Tag, aufrichtig und lustig.
4. 리액트 라이프사이클과 컴포넌트 Key값의 상호작용 이슈 본문
1. 발생 이슈
새로운 블록이 추가되는 기능을 구현하던 도중 컴포넌트의 key값과 관련하여 문제가 발생했다.
블록 추가 버튼을 눌렀을 때 기대했던 것은 한개의 블록이 추가되는 것이었는데,
실제로는 동일한 idx값을 가진 두 개, 세 개, 때로는 네 개의 다수의 블록이 동시에 추가되는 현상이 발생했다.
빠르게 버튼을 연타하지도 않았기 때문에
블록의 상태 관리와 추가되는 새 블록의 상태관리 로직을 살펴보고 콘솔로도 확인을 해보며 디버깅을 했다.
2. 문제 이유
블록이 렌더링 될 때마다 key값이 새로 할당 되는 로직이 문제였다.
리액트에서는 key라는 속성을 활용해서 자식 컴포넌트들을 유니크하게 식별한다.
그렇기 때문에 렌더링할 때마다 모든 블록 컴포넌트의 key 값이 변경 되면 리액트가 올바르게 컴포넌트를 식별할 수 없게 된다.
그러므로 리액트가 각 컴포넌트를 효율적으로 추적하고 렌더링하는 데 방해가 되어 이와 같은 이슈가 발생한 것이었다.
3. 해결 방법
해결책은 간단했다.
Block 컴포넌트의 key 값으로 block.id를 사용하고, 이 id는 블록이 처음 생성될 때 한 번만 설정하도록 코드를 변경했다.
즉 각 블록의 key값이 렌더링에 의해 변동되지 않고 마운트단계에서 고유한 값을 갖도록 수정했다.
그 결과, 블록의 key는 블록이 리스트에서 사라질 때까지 일관성을 유지하게 되었다.
리액트는 각 컴포넌트를 정확하게 식별하고 렌더링 할 수 있게 되어 블록추가 기능이 정상적으로 작동하게 되었다.
* 리액트 컴포넌트의 생명주기는 크게 세 단계로 나눌 수 있다.
마운트 (Mounting): 컴포넌트가 생성되고 화면에 나타나는 단계
업데이트 (Updating): 컴포넌트의 상태나 속성(props)이 변경되어 다시 렌더링되는 단계
언마운트 (Unmounting): 컴포넌트가 화면에서 사라지는 단계
4. 정리
컴포넌트에 key값을 할당할 때는 고유한 값을 할당하는 것이 필요하다.
컴포넌트를 효율적으로 렌더링하는 데 영향을 미치기 때문이다.
아래는 문제 해결에 과정에서 수정한 코드이다.
수정 전
const addBlock = (index) => {
const newBlock = { id: Math.random(), design: 'default' }; // add unique id
setBlocks((prevBlocks) => {
const newBlocks = [...prevBlocks];
newBlocks.splice(index + 1, 0, newBlock);
return newBlocks;
});
};
console.log(blocks, 'blocks');
console.log(newBlocks, 'newBlocks');
return (
<>
<Nav isLoading={isLoading} setIsLoading={setIsLoading} type='편집' />
{blocks?.map((block) => (
<Block key={`${page_idx}_${new Date().getTime()}`} idx={`${page_idx}_${new Date().getTime()}`} design={block.design} isOpen={isOpen} setIsOpen={setIsOpen} addBlock={addBlock} />
))}
<EditorModal isOpen={isOpen} setIsOpen={setIsOpen} />
</>
);
};
수정 후
const addBlock = (index) => {
const newBlock = { id: `${page_idx}_${new Date().getTime()}`, design: 'default' };
setBlocks((prevBlocks) => {
const newBlocks = [...prevBlocks];
newBlocks.splice(index + 1, 0, newBlock);
return newBlocks;
});
};
return (
<>
<Nav isLoading={isLoading} setIsLoading={setIsLoading} type='편집' />
{blocks?.map((block,idx) => (
<Block key={block.id} idx={idx} design={block.design} isOpen={isOpen} setIsOpen={setIsOpen} addBlock={addBlock} />
))}
<EditorModal isOpen={isOpen} setIsOpen={setIsOpen} />
</>
);
};
export default Editor;
'React' 카테고리의 다른 글
6. custom hook (0) | 2023.10.20 |
---|---|
5. Form (0) | 2023.10.01 |
3. PropTypes (0) | 2023.08.18 |
2. state (0) | 2023.03.16 |
1. jsx 문법 (0) | 2023.03.16 |