React/React 문법

[React]children 함수(map, forEach, toArray, count, only)

DevStory 2021. 10. 19.

React에서 props.children을 다양하게 조작할 수 있는 함수들을 소개합니다.

 


props.children을 순회하는 map, forEach 함수

React.Children.map은 자바스크립트 Array.prototype.map과 유사하지만, 동일하지는 않습니다.

 

React.Children.map은 React에서 props.children을 다루기 위한 독자적인 API이며, React.Children.forEach 또한 마찬가지입니다.

 

React.Children.map은 각 자식을 순회하여 함수를 호출하며, 함수가 반환하는 값들을 배열로 생성합니다.

 

다음은 props.children을 순회하여 index를 요소로 하는 배열을 생성하는 코드입니다.

class Category extends React.Component {
  render() {
    const mapArr = React.Children.map(this.props.children, function (
      child, // this.props.children의 개별 자식 요소입니다.
      idx // index입니다.
    ) {
      return idx;
    });
    console.log(mapArr);
    return <ul>{this.props.children}</ul>;
  }
}

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

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

실행 결과

React.Children.map 함수를 공식 문서에서 잘못 설명하고 있는 내용이 있는데, Fragment는 단일 자식으로 취급되어 순회하지 않는다고 설명합니다.

 

하지만, 다음 코드에서 <React.Fragment>를 자식 취급하고 있는 것을 확인할 수 있습니다.

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

class App extends React.Component {
  render() {
    return (
      <Category>
        <React.Fragment />
        <React.Fragment />
        <React.Fragment />
        <React.Fragment />
      </Category>
    );
  }
}

실행 결과

참고로 <> 빈 태그도 자식으로 취급합니다.

 

다음은 React.Children.forEach 함수를 사용하는 코드이며, 사용 방법은 React.Children.map 함수와 유사합니다.

 

차이점은 새로운 배열을 반환하지 않으므로 두 번째 매개변수인 함수에서 return 문을 작성할 필요가 없습니다.

class Category extends React.Component {
  render() {
    const mapArr = React.Children.forEach(this.props.children, function (
      child,
      idx
    ) {
      console.log(child);
      return idx; // <-- 필요없는 코드입니다.(map처럼 사용 불가능하다는 것을 보여주기 위해 작성한 코드)
    });
    console.log(mapArr);
    return <ul>{this.props.children}</ul>;
  }
}

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

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

실행 결과

① this.props.children를 순회하여 개별 자식 요소를 콘솔에 출력합니다.

② React.Children.forEach 함수는 반환하는 값이 없으므로 undefined를 출력합니다.


개수와 관련 있는 count, only 함수

React.Children.count 함수는 자식의 개수를 반환하는 함수입니다.

 

props.children을 매개변수로 전달하며, 자식이 존재하지 않은 경우 0을 반환합니다.

class Category extends React.Component {
  render() {
    console.log(
      "React.Children.count(this.props.children) : " +
        React.Children.count(this.props.children)
    );
    return <ul>{this.props.children}</ul>;
  }
}

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

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

실행 결과

React.Children.only 함수는 자식이 한 개가 아닐 경우 오류를 발생시킵니다.

 

한 개일 경우에만 정상적으로 동작하므로 자식이 0개인 경우 오류가 발생합니다.

 

props.children을 인수로 받으며, 자식을 반환합니다.

 

다음 코드는 자식이 한 개이므로 오류가 발생하지 않습니다.

class Category extends React.Component {
  render() {
    console.log(React.Children.only(this.props.children));
    return <ul>{this.props.children}</ul>;
  }
}

class App extends React.Component {
  render() {
    return (
      <Category>
        <li>HI</li>
      </Category>
    );
  }
}

 

다음 코드는 자식이 두 개이므로 오류가 발생합니다.

class App extends React.Component {
  render() {
    return (
      <Category>
        <li>HI</li>
        <li>HI2</li>
      </Category>
    );
  }
}

실행 결과

React.Children.only 함수의 문제점은 두 가지가 존재합니다.

  1. 하위 컴포넌트를 동적으로 추가하는 경우 배열의 요소가 한 개인데 오류가 발생합니다.
  2. JSX가 공백 검사에 엄격하기 때문에 공백을 제대로 제거하지 않은 경우 오류가 발생합니다.

 

다음은 Category 컴포넌트의 하위 요소를 동적으로 추가하는 코드입니다.

const li_Array = ["First item."];

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

 

배열의 요소가 한 개인데, React.Children.only에서 오류가 발생하는 문제가 있습니다.

 

위 문제를 해결하기 위해 동적으로 컴포넌트를 추가하는 코드를 <React.Fragment>로 감싸줍니다.

class Category extends React.Component {
  render() {
    console.log("this.props.children");
    console.log(this.props.children);
    console.log("this.props.children.props.children");
    console.log(this.props.children.props.children);
    return <ul>{React.Children.only(this.props.children)}</ul>;
  }
}

const li_Array = ["First item."];

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

실행 결과

① this.props.children 출력 결과입니다.

② this.props.children.props.children 출력 결과입니다.

 

위 해결 방법의 주의사항으로 동적으로 추가된 컴포넌트는 자식이 아니라 자손이므로 props.children.props.children으로 접근해야 합니다.

 

콘솔에서 props.children과 props.children.props.children의 값이 다른 것을 알 수 있습니다.


배열을 반환하는 toArray 함수

React.Children.toArray 함수는 자식을 요소로 가지는 새로운 배열을 반환합니다.

class Category extends React.Component {
  render() {
    console.log(React.Children.toArray(this.props.children));
    return <ul>{this.props.children}</ul>;
  }
}

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

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

실행 결과

React.Children.toArray 함수가 반환하는 새로운 배열을 이용하여 요소를 추가, 제거, 정렬 등 자바스크립트 배열에서 지원하는 함수를 사용할 수 있습니다.

 

이러한 작업은 React.Children.map 함수를 사용하여 새로운 배열을 생성해서 진행할 수 있지만, 새로운 배열을 생성한다는 관점에서는 map 함수보다 toArray 함수를 사용하는 코드가 더 간결합니다.

 

다음 코드는 자식을 배열로 생성 후 reverse함수로 배열의 요소를 반전한 값을 화면에 출력하는 코드입니다.

class Category extends React.Component {
  render() {
    const childArr = React.Children.toArray(this.props.children).reverse();
    return <ul>{childArr}</ul>;
  }
}

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

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

실행 결과

반응형

댓글