[React] JSX limitation, Fragment
JSX의 루트 요소는 1개여야 한다.
JSX의 한 제한 사항으로는 루트 수준에서 JSX 요소는 1개여야 한다는 것이다.
즉, 반환하거나 상수에 저장하는 값은 하나여야 한다는 것이다.
return (
<h2>Hi</h2>
<p>This does not work</p>
);
위 코드처럼 루트 수준에서 2개의 JSX 요소가 인접해 있으면 오류가 발생한다. JSX코드는 React.createElement 코드로 변환되는데,
return (
React.createElement('h2', {}, 'H1')
React.createElement('p', {}, 'This does not work')
);
자바스크립트에서는 두 개 이상을 반환할 수 없기 때문에 단 1개의 createElement만 반환되어야 하는 것이다.
따라서 다음과 같이 인접한 요소들을 div나 다른 element, 또는 사용자 정의 컴포넌트로 감싸면 된다.
return (
<div>
<h2>Hi</h2>
<p>This does not work</p>
</div>
);
<div> soup
하지만 div나 다른 요소로 감쌀 때는 div soup라는 문제가 발생한다.
<div>
<div>
<div>
<h2>Div Soup!!!</h2>
</div>
</div>
</div>
컴포넌트들은 JSX의 제한 사항이나 여러가지 이유로 div로 감싸질 수 있는데, 이렇게 의미가 없는 불필요한 div들이 실제 DOM으로 렌더링된다.
너무 많은 HTML 요소를 렌더링하면 애플리케이션이 느려질 수도 있고, 감싸는 div가 있을 때 중첩된 css 선택자를 사용한다면 스타일링이 깨질 수도 있다.
해결방법
1. 사용자 정의 Wrapper 컴포넌트 생성
const Wrapper = props => {
return props.children;
};
export default Wrapper;
props.children은 컴포넌트에서 여는 태그와 닫는 태그 사이에 있는 내용을 반환한다. 이 Wrapper 컴포넌트는 "빈" 컴포넌트이다.
return (
<Wrapper>
<h2>Hi</h2>
<p>This does not work</p>
</Wrapper>
);
Wrapper 컴포넌트를 import하고 사용하면 된다.
이 요소는 DOM에 아무것도 렌더링하지 않는다. 하지만 리액트의 요구사항인 하나의 루트 요소를 렌더링해야 하는 것이므로, Wrapper가 그 하나의 루트 요소이기 때문에 요구사항을 만족해서 잘 작동한다.
2. React.Fragment
리액트에서 Fragment를 제공하기 때문에 위의 Wrapper 컴포넌트를 사용자가 직접 정의하지 않아도 된다.
import React from 'react';
...
return (
<React.Fragment>
<h2>Hi</h2>
<p>This does not work</p>
</React.Fragment>
);
또는 빌드 워크플로가 이를 지원하면 빈 태그를 쓰면 된다.
return (
<>
<h2>Hi</h2>
<p>This does not work</p>
</>
);
Frament는 Wrapper와 마찬가지로 빈 wrapper 컴포넌트이다. 실제 HTML 요소를 DOM에 렌더링하지 않는다.
따라서 프래그먼트를 사용하면 불필요한 HTML 요소들을 줄일 수 있다!