고양이와 코딩
[React] - 컴포넌트 순수성 유지 본문
https://react-ko.dev/learn/keeping-components-pure
리액트 공식문서 + 함수형 코딩스터디를 동시에 하며 도움이 되어서 포스팅 합니다 !
컴포넌트를 엄격하게 순수 함수로 작성하기 위한 규칙
- 호출되기 전에 존재했던 객체나 변수를 변경하지 않습니다
- 동일한 입력이 주어지면 항상 동일한 결과를 반환해야 합니다.
→ React는 이 개념을 중심으로 설계되었기에, 우리가 작성하는 모든 컴포넌트가 순수 함수라고 가정한다.
function Recipe({ drinkers }) {
return (
<ol>
<li>Boil {drinkers} cups of water.</li>
<li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
<li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
</ol>
);
}
export default function App() {
return (
<section>
<h1>Spiced Chai Recipe</h1>
<h2>For two</h2>
<Recipe drinkers={2} />
<h2>For a gathering</h2>
<Recipe drinkers={4} />
</section>
);
}
위 컴포넌트는 drinkers를 props로 받아(직접적인 접근x) 리스트에 전달하고 같은 입력값에 대해 항상 같은 결과를 출력합니다.
let Drinkers;
function Recipe() {
return (
<ol>
<li>Boil {Drinkers} cups of water.</li>
<li>Add {Drinkers} spoons of tea and {0.5 * Drinkers} spoons of spice.</li>
<li>Add {0.5 * Drinkers} cups of milk to boil and sugar to taste.</li>
</ol>
);
}
export default function App() {
Drinkers = 2;
return (
<section>
<h1>Spiced Chai Recipe</h1>
<h2>For two</h2>
<Recipe />
{Drinkers = 4}
<h2>For a gathering</h2>
<Recipe />
</section>
);
}
하지만 Drinkers가 전역 변수로 선언되었고, 이를 사용한다면 계산이 수행됨에 따라 변수의 값이 달라지므로 결과가 예측할 수 없게 됩니다. 이는 사이드이펙트(의도하지 않은 결과)를 발생시킵니다
React에서 렌더링 하는 동안 읽을 수 있는 입력 세가지
- props
- state
- context
순수성 유지를 위해 이 세 입력은 항상 `읽기 전용`으로 취급해야 합니다
사용자 입력에 대한 응답으로 무언가 변경시키려면 변수를 변경하는 대신 state를 설정해야 합니다
(컴포넌트가 렌더링 되는 동안 기존 변수나 객체를 변경해서는 안됩니다 !)
렌더링 하는 동안, `방금` 생성한 변수와 객체를 변경하는 것은 괜찮습니다
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaGathering() {
let cups = [];
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />);
}
return cups;
}
여기에서 cups는 함수 내부에 존재하기 때문에 변경해도 컴포넌트가 순수성을 잃지 않습니다 (지역변수는 괜찮다!)
사이드 이펙트를 일으킬 수 있는 곳
- 화면 업데이트
- 애니메이션 시작
- 데이터 변경
- 이메일 보내기
- .......
프로그래밍에서 사이드 이펙트 없이 뭔가를 만드는건 불가합니다 !
(`쏙쏙 들어오는 함수형 코딩` 에서는 이메일을 보내는 시스템을 개발하는데 이메일을 보내지 않을 수는 없다고 말합니다)
React에서는 보통 이런 사이드 이펙트가 이벤트 핸들러에 속합니다! (예: 버튼 클릭)
이런 이벤트 핸들러들은 컴포넌트 내부에 정의되지만, 실제로는 해당 컴포넌트가 렌더링 될 때 실행되지 않습니다.
대신, 사용자가 이벤트를 발생시키는 동작을 할 때만 실행됩니다.
→ 컴포넌트가 렌더링 될 때 마다 실행되는게 아니기 때문에 이벤트 핸들러는 순수 함수일 필요가 없습니다.
그렇다면 useEffect 사용은 지양해야 할까?
위 내용을 읽고, useEffect는 렌더링이 될 때마다 실행되기 때문에 많은 부수 효과를 발생시키지 않나? 라는 생각이 들었습니다.
실제로 문서에서는 모든 옵션을 고려하고도 사이드 이펙트에 적합한 이벤트 핸들러를 찾을 수 없다면 useEffect를 사용하라고 말합니다
→ 하지만 useEffect를 아예 사용하지 않을 수 없으므로, 최대한 다양한 경우의 수를 생각한 후 최후의 수단으로 사용하길 권장합니다!
요약
- 컴포넌트는 순수해야 합니다:
- 자신의 일에만 신경씁니다. 렌더링 전에 존재했던 객체나 변수를 변경하지 않아야 합니다.
- 동일한 입력, 동일한 출력. 동일한 입력이 주어지면 컴포넌트는 항상 동일한 JSX를 반환해야 합니다.
- 렌더링은 언제든지 발생할 수 있으므로, 컴포넌트는 서로의 렌더링 순서에 의존해서는 안 됩니다.
- 컴포넌트가 렌더링에 사용하는 어떠한 입력값도 변이해서는 안 됩니다. 여기에는 props, state 및 context가 포함됩니다. 화면을 업데이트하려면 기존 객체를 변이하는 대신 “set” state를 사용하세요.
- 컴포넌트의 로직을 반환하는 JSX 안에 표현하기 위해 노력하세요. “무언가를 변경”해야 할 때는 보통 이벤트 핸들러에서 이 작업을 수행하고자 할 것입니다. 최후의 수단으로 useEffect를 사용할 수도 있습니다.
- 순수 함수를 작성하는 데는 약간의 연습이 필요하지만, React 패러다임의 힘을 발휘할 수 있습니다.
'react' 카테고리의 다른 글
[React] - state의 업데이트 (3) | 2024.03.15 |
---|---|
[React] - useState와 useRef (0) | 2024.03.06 |
[React] - TypeScript : Record 유틸리티 타입 (0) | 2024.02.25 |
코드 스니펫을 사용해보자 ! (snippet-generator) (2) | 2024.01.31 |
[React] - 서버 사이드 렌더링에서의 데이터 로딩 (Redux- thunk) (1) | 2024.01.02 |