React/React 문법

[React]props.children에 props 전달

DevStory 2021. 10. 20.

이번 포스팅에서는 props.children에 데이터를 전달하는 방법을 소개합니다.

 

React에서 props는 읽기 전용이므로 수정이 불가능하며, 마찬가지로 props.children도 수정할 수 없습니다.

 

만약, props.children을 수정하고 싶다면, Reac.cloneElement 함수를 사용하여 자식 컴포넌트를 복제 후 수정하거나 React.Children.toArray 또는 React.Children.map 함수를 사용하여 새로운 배열을 생성 후 수정할 수 있습니다.

 

그럼 하나씩 살펴보도록 하겠습니다.

 


React 엘리먼트를 복제하는 React.cloneElement 함수

React.cloneElement 함수를 설명하기 전에 props를 조작하면 어떤 오류가 발생하는지 살펴보겠습니다.

 

다음은 읽기 전용인 props의 속성을 변경 후 오류가 발생하는 예제입니다.

class Test extends React.Component {
  render() {
    return <h1>Test</h1>;
  }
}

class App extends React.Component {
  render() {
    const element = <Test testProps="HI" />;

    console.log("element.props.testProps : " + element.props.testProps);

    // Cannot assign to read only property 'testProps' of object '#<Object>'
    element.props.testProps = "Update";

    return element;
  }
}

실행 결과

props의 testProps는 읽기 전용이므로 값 할당을 할 수 없다는 에러가 발생합니다.

 

JSX 객체는 생성되면 변경할 수 없는 객체이므로 props의 속성뿐만 아니라 다음 코드처럼 속성을 추가하는 것도 불가능합니다.

class App extends React.Component {
  render() {
    const element = <Test testProps="HI" />;

    // Cannot add property testProps, object is not extensible
    element.testProps = 1;

    return element;
  }
}

하지만, 복제는 가능하므로 복제하는 과정에서 props의 속성을 추가 및 변경할 수 있습니다.(삭제는 안됩니다.)

 

복제하는 방법은 React의 cloneElement 함수를 사용합니다.

const cloneObj = React.cloneElement(element[, props[, ...children]]);

React.cloneElement 함수는 첫 번째 인수(element)의 type과 props가 동일한 새로운 엘리먼트 객체를 반환합니다.

 

두 번째 인수(props)는 생략 가능한데, 두 번째 인수를 작성하면 기존 props에 새로운 속성을 추가합니다.

 

다음은 React.cloneElement 함수를 사용하여 JSX 객체를 복제하고 props의 속성의 값을 변경 및 새로 추가하는 예제입니다.

class App extends React.Component {
  render() {
    const element = <Test testProps="HI" />;

    const cloneObj = React.cloneElement(element, {
      testProps: "Hello",
      addProps: "Add"
    });

    console.log("element");
    console.log(element);
    console.log("cloneObj");
    console.log(cloneObj);

    return element;
  }
}

실행 결과

기존에는 props.testProps 속성만 존재했는데, React.cloneElement 함수를 사용하여 addProps 속성을 추가하였으며, props.testProps 속성의 값을 변경하였습니다.


props.children 복제

다음 코드는 Category 자식 컴포넌트를 복제하여 props.sort 속성을 추가하는 예제입니다.

class Category extends React.Component {
  render() {
    console.log(`this.props.children`);
    console.log(this.props.children);
    const cloneArr = React.Children.map(this.props.children, (child, idx) =>
      React.cloneElement(child, { sort: idx })
    );
    console.log(`React.cloneElement`);
    console.log(cloneArr);
    return <ul>{cloneArr}</ul>;
  }
}

const li_Array = ["First item.", "Second item."];

class App extends React.Component {
  render() {
    return (
      <Category>
        {li_Array.map((value, idx) => (
          <li key={idx}>{value}</li>
        ))}
      </Category>
    );
  }
}

실행 결과

① this.props.children이며, props.sort가 존재하지 않습니다.

② this.props.children을 복제하여 props.sort를 추가하였습니다.

 

위 예제는 props.children를 React.cloneElement 함수를 사용하여 복제하였으며 모든 자식 엘리먼트에 props.sort를 추가하는 아주 단순한 예제입니다.

 

실용적인 코드는 아니며, 차라리 아래 코드처럼 동적으로 li 태그를 추가하는 코드에 sort 속성을 추가하는 것이 훨씬 깔끔합니다.

class App extends React.Component {
  render() {
    return (
      <Category>
        {li_Array.map((value, idx) => (
          <li key={idx} sort={idx}>{value}</li>
        ))}
      </Category>
    );
  }
}

다음은 props.children을 좀 더 실용성 있게 다루는 방법을 설명합니다.


props.children 조작

props.children은 자식 엘리먼트가 어떻게 구성되어있는지 모르는 경우 주로 사용합니다.

 

자식 엘리먼트의 개수가 몇 개인지? 컴포넌트인지? 기본 html 엘리먼트인지?

 

자식 엘리먼트가 무엇이든 될 수 있기 때문에 데이터 유형의 일관성이 없습니다.

 

이러한 경우 자식 엘리먼트를 새로운 배열로 반환하는 React.Children.toArray 함수와 엘리먼트를 복제하는 React.cloneElement 함수가 도움을 줄 수 있습니다.

 

다음은 자식 엘리먼트의 개수가 동적이므로 몇 개 렌더링될지 모르며, 2개 이상인 경우 0번째 자식의 className을 top, 마지막 자식의 className을 bottom으로 설정하는 예제입니다.

class Category extends React.Component {
  render() {
    let childArray = React.Children.toArray(this.props.children);

    if (childArray.length >= 2) {
      let lastIdx = childArray.length - 1;

      let firstChild = React.cloneElement(childArray[0], {
        className: "top"
      });

      let lastChild = React.cloneElement(childArray[lastIdx], {
        className: "bottom"
      });

      childArray[0] = firstChild;
      childArray[lastIdx] = lastChild;

      console.log(childArray)
    }
    return <ul>{childArray}</ul>;
  }
}

const li_Array = ["First item.", "Second item.", "Another item."];

class App extends React.Component {
  render() {
    return (
      <Category>
        {li_Array.map((value, idx) => (
          <li className="center" key={idx}>
            {value}
          </li>
        ))}
      </Category>
    );
  }
}

코드를 더 깔끔하게 구현할 수 있지만, 동작하는데 큰 문제는 없습니다.

 

하지만, className의 기본 값이 top 또는 bottom인 경우에는 코드를 개선하는 작업이 필요합니다.

 

이렇게 자식 엘리먼트의 개수를 모르며, props의 속성을 변경해야 하는 경우 위 방법처럼 React.Children.toArray 함수와 React.cloneElement 함수를 사용할 수 있습니다.

반응형

댓글