React

[React] JSX limitation, Fragment

ssohyunn 2023. 9. 13. 13:55

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 요소들을 줄일 수 있다!