리액트 클린코드 - Props
TIL 24. 03. 04
클린코드 리액트 - Props
불필요한 Props 복사
다음과 같이 불필요하게 props를 복사해서 사용하는 것을 지양해야 한다.
function Component({ value }) {
const [copyValue] = useState(value);
return <div>{value}</div>
}
상태로 만들 필요 없이 변수에 저장해서 사용하면 된다. 부모 컴포넌트에서 props이 업데이트되면 자식 컴포넌트 역시 리렌더링 되기 때문이다. 그런데 자식 컴포넌트에서 props를 조작해야 하는 경우가 있다면? 심지어 값 비싸고 무거운 연산이라면?
function Component({ value }) {
const calculatedValue = heavyCalculatFn(value);
return <div>{value}</div>
}
렌더링 될 때마다 무거운 연산이 수행될 것이다. 이런 경우에는 useMemo
를 사용하면 된다.
function Component({ value }) {
const calculatedValue = useMemo(() => heavyCalculatFn(value), [value])
return <div>{value}</div>
}
권장 사항은 부모 컴포넌트에서 연산을 수행하고 그 결과를 자식 컴포넌트에게 전달하는 것이다. 그 이유는 리액트는 데이터가 한 방향으로 흘러가 데이터를 통제하기 쉽고, 애플리케이션의 상태를 예측하기가 더 수월하기 때문이다. 자식 컴포넌트에서 props로 연산을 해야 한다면 useMemo
를 사용하거나 컴포넌트 내부의 변수로 만들어 간단히 사용할 수 있다.
Shorthand Props
shorthand props는 shorthand props는 부모 컴포넌트로부터 받은 props를 자식 컴포넌트에 더 간결하게 전달하는 방법이다. 하위 컴포넌트로 props를 단순히 전달하는 경우 자바스크립트의 스프레드 연산자를 활용할 수 있다.
function Person(props) {
return (
<p>
{props.name} is {props.age} years old
</p>
)
}
function App() {
const person = { name: 'Kim', age: 21 }
return <Person {...person} />
}
단, 주의할 점은 코드를 예측하기 어려워진다는 것이다. 자식 컴포넌트에서는 어떤 props가 전달되었는지 확인하려면 부모 컴포넌트로 올라가야 한다. 마치 props drilling과 같은 효과를 유발할 수 있다. 때로는 명시적으로 개별 props를 전달하는 것이 더 안전하고 이해하기 쉬울 수 있다.
또한, 불필요한 props 또는 변경되지 않는 props가 자주 전달될 경우 렌더링 성능에도 좋지 않다.
좋은 Props 네이밍
1️⃣ 자바스크립트의 변수나 함수 이름에는 CamelCase 표기법을 사용하므로 키-값의 형태로 전달하는 props 역시 이를 따른다.
2️⃣ 리액트에서는 컴포넌트는 대문자로 시작하는 PascalCase 표기법을 사용한다. 따라서, props로 전달하는 속성명을 대문자로 사용한다면 컴포넌트가 전달될 것이라고 기대한다.
3️⃣ 잘못된 부분은 아니지만, ={true}
는 생략할 수 있다. JSX에서 prop 이름만 전달하면 그 prop은 자동으로 true
로 간주한다.
function Component() {
return (
<ChildComponent
component-text="value" // 1️⃣ componentText="value"
otherComponent={OtherComponent} // 2️⃣ OtherComponent={OtherComponent}
isShow={true} // 3️⃣ isShow
/>
)
}
인라인 스타일 주의하기
리액트에서 스타일을 인라인으로 사용할 때 주의해야 할 점이 있다. HTML에서는 문제가 없지만 리액트는 JSX 문법을 사용하므로 아래의 방법으로는 동작하지 않는다.
function Button() {
return (
<button style={"background-color: 'red'; font-size: '14px';}>
버튼
</button>
)
}
JSX 문법은 ‘자바스크립트’로 HTML을 표현할 수 있는 문법임을 기억해야 한다. 자바스크립트 키(속성)는 카멜 케이스를 사용하고 여러 개의 키-값은 ,
로 구분한다. 또한, 자바스크립트에서 표현식에 객체를 넣을 때는 중괄호를 사용한다.
function Button() {
return (
<button style=>
버튼
</button>
)
}
객체를 전달하면 렌더링 될 때마다 평가되기 때문에 불필요한 연산이 일어난다. 수정되지 않는 객체라면 컴포넌트 외부에 선언하는 것이 좋다.
const ButtonStyle = {
warning: { backgroundColor: 'yellow' },
danger: { backgroundColor: 'red' }
}
function Button() {
return (
<button style={ButtonStyle.warning}>
버튼
</button>
)
}
객체 Props 지양해야 하는 이유
props로 객체를 전달하면 값이 변경되지 않아도 자식 컴포넌트는 리렌더링 되어 성능에 영향을 준다.
Object.is(
{ hello: "world" },
{ hello: "world" },
); // false
Object.is(["hello"], ["hello"]); // false
리액트는 객체를 비교할 때 Object.is
를 사용하기 때문이다. 비교하는 두 객체가 같다면 객체의 ‘참조 값’(객체가 저장되어 있는 메모리 주소)이 동일하다는 것을 의미한다. 실제로 위의 예시에서 보기에는 두 객체가 동일하지만 참조하고 있는 주소가 다르기 때문에 false를 반환하였다.
객체 props를 부모 컴포넌트에서 재생성하여 전달할 때마다 새 참조가 생성되기 때문에, 실제 내용에 변화가 없어도 리렌더링이 발생한다.
권장사항은 다음과 같다.
- 변하지 않는 값을 경우 컴포넌트 외부로 내보낸다.
- 필요한 값만 객체를 분해해서 props로 전달한다.
useMemo
를 사용한다. 단, 무거운 연산이나 잦은 연산이 있을 경우 사용할 것.- 컴포넌트를 나눈다.
객체보다 단순한 Props의 장점
객체 전체를 props로 전달하기보다 단순한 props를 전달하는 것을 고려해보자. 정말 필요한 데이터만 받아오고 있는지, props로 전달받은 객체가 다시 생성되는 경우에 컴포넌트가 불필요하게 렌더링되지 않는지.
function UserInfo({ user }) {
return (
<div>
<img src={user.avatarImgUrl} />
<h3>{user.userName}</h3>
<h4>{user.email}</h4>
</div>
)
}
사용하지 않을 props까지 전달하기보다 객체의 구조를 분해해서 전달하면 최적화(memo)가 더 쉽고 가져오는 props가 더 명확해진다. 게다가 불필요한 렌더링도 줄일 수 있다. 원시값으로 전달한다면 유리한 점이 많다. (데이터 유형 구분, 불변성, 비교 간단 등)
function UserInfo({ avatarImgUrl, userName, email }) {
return (
<div>
<img src={avatarImgUrl} />
<h3>{userName}</h3>
<h4>{email}</h4>
</div>
)
}
댓글남기기