Props로 컴포넌트를 전달하는 방법

React에서 컴포넌트에 컴포넌트를 전달할 때 보통 children 속성을 이용하지만, 별도로 정의한 Props로 컴포넌트를 넘길 때에는 상황에 맞게 Props를 다른 방법으로 전달해야 함. 

 

전달하려는 컴포넌트를 ① 함수형 컴포넌트로 정의, ② JSX 형태를 직접 변수로 정의하는 두 가지 방법이 있고,

// 방법1
const ChildComponent = () => {
   return <p>자식 컴포넌트</p>
}

// 방법2
const childComponent = <p>자식 컴포넌트</p>

컴포넌트를 Props로 전달하는 방법에는 ① 컴포넌트 자체를 전달, ② JSX 형태로 전달, ③ (props) => JSX.Element 형태로 화살표 함수로 래핑해서 전달하는 방법이 있음.

<MyComponent item={ChildComponent} />
<MyComponent item={<ChildComponent />} />
<MyComponent item={() => <ChildComponent />} />
<MyComponent item={(props) => <ChildComponent {…props} otherProps={0} />} />

마지막으로 Props로 전달받은 컴포넌트를 렌더링하는 방법에는 ① children 속성처럼 객체 형식으로 렌더링, ② JSX 형식으로 렌더링하는 방법이 있음.

const MyComponent = ({ item }) => <div>{item}</div>;

const MyComponent = ({ Item }) => (
  <div>
    <Item />
  </div>
);

const MyComponent = ({ Item }) => (
   <div>
      <Item itemProp1="a" itemProp2={0} />
   </div>
);

 

 

 

1. React.createElement()

컴포넌트를 전달하고 렌더링하는 방법도 여러가지라서 헷갈릴 수 있지만 React에서 JSX는 createElement() 메서드를 호출하는 Syntactic Sugar라는 점만 기억하면 헷갈리지 않을 수 있음. #

createElement(type, props, ...children)

① type: HTML 태그명(div, p, span, ...), React 함수형/클래스형 컴포넌트, React Fragment(<></>)

② props: 컴포넌트에 전달할 Props 객체

③ children      

 

createElement는 위와 같은 세 가지 값을 parameter로 가지며, 실행했을 때 type, key, ref , props 속성을 가지는 React Element 객체를 반환함.

// 방법1
const ChildComponent = () => {
   return <p>자식 컴포넌트</p>
}

// 방법2
const childComponent = <p>자식 컴포넌트</p>

위와 같이 정의된 ChildComponent는 Babel로 변환되면 아래와 같이 표현될 수 있음. 즉, 함수로 정의된 컴포넌트와 JSX로 바로 정의된 컴포넌트는 createElement() 메서드를 실행했느냐 아니냐 하는 차이점밖에 없음.

const ChildComponent = () => /*#__PURE__*/React.createElement("p", null, "자식 컴포넌트");

const childComponent = /*#__PURE__*/React.createElement("p", null, "자식 컴포넌트");

 

React에서 <Component />는 React Element 객체

 

 

 

2. Props로 전달한 컴포넌트를 렌더링하는 방법

그렇다면 ChildComponent를 MyComponent에서 렌더링하는 방법은 어떤 차이점이 있을까?

const MyComponent = ({ item }) => <div>{item}</div>;

const MyComponent = ({ Item }) => (
  <div>
    <Item />
  </div>
);
const MyComponent = ({
  item
}) => /*#__PURE__*/React.createElement("div", null, item);

const MyComponent = ({
  Item
}) => /*#__PURE__*/React.createElement(
   "div", 
   null, 
   /*#__PURE__*/React.createElement(Item, null)
);

 

 

A. {item}으로 컴포넌트를 렌더링

이렇게 전달된 item은 MyComponent 컴포넌트에서 children 속성으로 전달되는데 children 속성은 React Element 객체 또는 그것들의 배열만 받을 수 있기 때문에 아래와 같은 상황에서만 사용할 수 있는 방법.

const childComponent = <p>자식 컴포넌트</p>
const MyComponent = ({ item }) => <div>{item}</div>

// ✅
<MyComponent item={childComponent} />
const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ item }) => <div>{item}</div>

// ✅
<MyComponent item={<ChildComponent />} />

children 속성으로 함수는 전달할 수 없기 때문에 아래와 같이 함수형 컴포넌트를 바로 전달하게 되면 위와 같은 오류가 발생하고 전달된 컴포넌트가 제대로 렌더링되지 않음. 

const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ item }) => <div>{item}</div>

// ❌
<MyComponent item={ChildComponent} />

 

한편, item으로 전달할 컴포넌트가 MyComponent 내부에서 별도의 Props를 받아야 한다면 {item}으로는 그러한 값을 전달할 수 있는 방법이 없기 때문에 {item}으로 렌더링하는 것은 적절한 방법이 아님.

 

 

B. <Item />으로 컴포넌트를 렌더링

이렇게 전달된 Item은 MyComponent에서 createElement()가 실행되는데 createElement()의 첫번째 parameter인 type에는 HTML 태그명(문자열), React 함수/클래스형 컴포넌트, React Fragment만 가능하기 때문에 

/*#__PURE__*/React.createElement(Item, null)

전달할 컴포넌트가 React Element 객체라면 화살표 함수를 래핑해서 전달해야 함.

const childComponent = <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

// ✅
<MyComponent Item={() => childComponent} />
const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

// ✅
<MyComponent Item={ChildComponent} />
const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

// ✅
<MyComponent Item={() => <ChildComponent />} />

 

const childComponent = <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

// ❌
<MyComponent Item={childComponent} />
const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

// ❌
<MyComponent Item={<ChildComponent />} />

<Item />으로 컴포넌트를 렌더링하는 컴포넌트에 React Element 객체를 전달하면 아래와 같은 오류 메시지가 뜸.

ChildComponent를 컴포넌트 내부에서 정의하는 것은 바람직하지 않음. 하지만, 컴포넌트 내부에서 정의해 메모이제이션해야 할 경우에는 useMemo, useCallback 둘 다 사용할 수 있지만 ChildComponent를 전달받은 부모 컴포넌트가 ChildComponent를 어떻게 렌더링하고 있는지에 따라 다른 Hook을 사용해야 함.

 

 

 

3. 전달할 컴포넌트에 추가적인 Props 전달하기

{item}으로 컴포넌트를 렌더링할 때는 전달받을 컴포넌트에 추가적인 Props를 전달할 수 없지만, <Item />으로 렌더링한다면 그것이 가능하다는 장점이 있음.

// 1-1. 추가적인 Props를 내부에서 전달할 수 있음
const MyComponent = ({ Item }) => {
   const [color, setColor] = useState("white")
   
   return (
      <div>
         <Item color={color} />
      </div>
   );
};
// 1-2. 컴포넌트 리스트를 맵핑할 때도 유용함
const ChildComponent = ({ text, color }) => (
   <p>
      {text}: {color}
   </p>
);

const MyComponent = ({ data, Item }) => {
   const [color, setColor] = useState("white");

   return (
      <div>
         {data.map(({ text }) => (
            <Item text={text} color={color} />
         ))}
      </div>
   );
};

<MyComponent
   Item={({ text, color }) => <ChildComponent text={text} color={color} />}
/>;

 

// 2. 컴포넌트를 전달하기 전에 추가적인 Props를 전달하는 것도 가능함
const ChildComponent = ({ text }) => <p>{text}</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

// ✅
<MyComponent Item={() => <ChildComponent text="자식 컴포넌트" />} />

 

 

 

4. 정리

① {item}: item은 객체여야 함

const ChildComponent = <p>자식 컴포넌트</p>
const MyComponent = ({ item }) => <div>{item}</div>

<MyComponent item={ChildComponent} />
const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ item }) => <div>{item}</div>

<MyComponent item={<ChildComponent />} />

 

② <Item />: Item은 함수, 클래스여야 함

const childComponent = <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

<MyComponent Item={() => childComponent} />
const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

<MyComponent Item={ChildComponent} />
const ChildComponent = () => <p>자식 컴포넌트</p>
const MyComponent = ({ Item }) => (
   <div>
      <Item />
   </div>
)

<MyComponent Item={() => <ChildComponent />} />

 

 

 

 

 

[참고]

How to Pass a Component as props in React