ReactJs: What should the PropTypes be for this.props.children?
Asked Answered
G

9

411

Given a simple component that renders its children:

class ContainerComponent extends Component {
  static propTypes = {
    children: PropTypes.object.isRequired,
  }

  render() {
    return (
      <div>
        {this.props.children}
      </div>
    );
  }
}

export default ContainerComponent;

Question: What should the propType of the children prop be?

When I set it as an object, it fails when I use the component with multiple children:

<ContainerComponent>
  <div>1</div>
  <div>2</div>
</ContainerComponent>

Warning: Failed prop type: Invalid prop children of type array supplied to ContainerComponent, expected object.

If I set it as an array, it will fail if I give it only one child, i.e.:

<ContainerComponent>
  <div>1</div>
</ContainerComponent>

Warning: Failed prop type: Invalid prop children of type object supplied to ContainerComponent, expected array.

Please advise, should I just not bother doing a propTypes check for children elements?

Guerdon answered 8/2, 2017 at 20:0 Comment(3)
You probably want nodeKeynes
Possible duplicate of only allow children of a specific type in a react componentFiligree
Please see my answer below that describes more options, but, if you are looking for component child then it's PropTypes.element. PropTypes.node describes anything that can be rendered - strings, numbers, elements or an array of these things. If this suits you then this is the way.Cresting
G
553

Try something like this utilizing oneOfType or PropTypes.node

import PropTypes from 'prop-types'

...

static propTypes = {
    children: PropTypes.oneOfType([
        PropTypes.arrayOf(PropTypes.node),
        PropTypes.node
    ]).isRequired
}

or

static propTypes = {
    children: PropTypes.node.isRequired,
}
Garver answered 8/2, 2017 at 20:7 Comment(5)
Unfortunately it fails with the same error in the single child case: "Warning: Failed prop type: Invalid prop children of type object...expected an array."Guerdon
That worked! The simplest solution is children: PropTypes.node, that worked for both cases. Thanks for the suggestions =)Guerdon
The only thing that would make this answer clearer would be if you included a note similar to @Cresting 's answer to explain that React.PropTypes.node describes any renderable object.Savell
There's no need for array, just PropTypes.node. That handles the following correcting: nothing, string, single element, several elements, fragment, component.Extroversion
I need to hang this answer somewhere on the wall of my room. I keep coming back here over and over again. :)Hoopla
C
62

For me it depends on the component. If you know what you need it to be populated with then you should try to specify exclusively, or multiple types using:

PropTypes.oneOfType 

If you want to refer to a React component then you will be looking for

PropTypes.element

Although,

PropTypes.node

describes anything that can be rendered - strings, numbers, elements or an array of these things. If this suits you then this is the way.

With very generic components, who can have many types of children, you can also use the below. However I wouldn't recommend it. As mentioned in the comments below, it does somewhat defeat the point of using PropTypes and there are usually other ways to specify what your component requires. Also bare in mind that eslint and ts may (probably) not be happy with this lack of specificity:

PropTypes.any
Cresting answered 8/2, 2017 at 20:36 Comment(3)
Proptypes.any is too common type. Eslint is not happy with that.Borman
Proptypes.any would also mean object and the app may crash if you pass an object.Stannite
If you're going to use any, you should just save yourself the time of using propTypes at all.Premiere
S
37

The PropTypes documentation has the following

// Anything that can be rendered: numbers, strings, elements or an array
// (or fragment) containing these types.
optionalNode: PropTypes.node,

So, you can use PropTypes.node to check for objects or arrays of objects

static propTypes = {
   children: PropTypes.node.isRequired,
}
Spannew answered 12/6, 2018 at 17:24 Comment(0)
A
19

The answers here don't seem to quite cover checking the children exactly. node and object are too permissive, I wanted to check the exact element. Here is what I ended up using:

  • Use oneOfType([]) to allow for single or array of children
  • Use shape and arrayOf(shape({})) for single and array of children, respectively
  • Use oneOf for the child element itself

In the end, something like this:

import PropTypes from 'prop-types'
import MyComponent from './MyComponent'

children: PropTypes.oneOfType([
  PropTypes.shape({
    type: PropTypes.oneOf([MyComponent]),
  }),
  PropTypes.arrayOf(
    PropTypes.shape({
      type: PropTypes.oneOf([MyComponent]),
    })
  ),
]).isRequired

This issue helped me figure this out more clearly: https://github.com/facebook/react/issues/2979

Attah answered 31/3, 2018 at 1:33 Comment(0)
S
6

If you want to match exactly a component type, check this

MenuPrimary.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(MenuPrimaryItem),
    PropTypes.objectOf(MenuPrimaryItem)
  ])
}

If you want to match exactly some component types, check this

const HeaderTypes = [
  PropTypes.objectOf(MenuPrimary),
  PropTypes.objectOf(UserInfo)
]

Header.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.oneOfType([...HeaderTypes])),
    ...HeaderTypes
  ])
}
Skyscraper answered 19/1, 2018 at 13:49 Comment(0)
C
6

If you want to include render prop components:

  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.node),
    PropTypes.node,
    PropTypes.func
  ])
Commune answered 30/11, 2020 at 5:18 Comment(1)
optionalElement: PropTypes.element is great also for passing...a React element, e.g. a React component that you create.Hydro
F
3

Try a custom propTypes :

 const  childrenPropTypeLogic = (props, propName, componentName) => {
          const prop = props[propName];
          return React.Children
                   .toArray(prop)
                   .find(child => child.type !== 'div') && new Error(`${componentName} only accepts "div" elements`);
 };


static propTypes = {

   children : childrenPropTypeLogic

}

Fiddle

const {Component, PropTypes} = React;

 const  childrenPropTypeLogic = (props, propName, componentName) => {
             var error;
          var prop = props[propName];
    
          React.Children.forEach(prop, function (child) {
            if (child.type !== 'div') {
              error = new Error(
                '`' + componentName + '` only accepts children of type `div`.'
              );
            }
          });
    
          return error;
    };
    
  

class ContainerComponent extends Component {
  static propTypes = {
    children: childrenPropTypeLogic,
  }

  render() {
    return (
      <div>
        {this.props.children}
      </div>
    );
  }
}



class App extends Component {
   render(){
    return (
    <ContainerComponent>
        <div>1</div>
        <div>2</div>
      </ContainerComponent>
    )
   }
}

ReactDOM.render(<App /> , document.querySelector('section'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<section />
Filigree answered 8/2, 2017 at 20:25 Comment(0)
H
1

Accoriding to propTypes documentation element is used for React element, and children is one of them:

// A React element.
optionalElement: PropTypes.element,

Then you can combine it with arrayOf

Component.propTypes = { children: PropTypes.arrayOf(PropTypes.element).isRequired, };

Then it will require at least one children.

Hydrometer answered 16/2, 2022 at 6:28 Comment(0)
C
0

Example:

import React from 'react';
import PropTypes from 'prop-types';

class MenuItem extends React.Component {
    render() {
        return (
            <li>
                <a href={this.props.href}>{this.props.children}</a>
            </li>
        );
    }
}

MenuItem.defaultProps = {
    href: "/",
    children: "Main page"
};

MenuItem.propTypes = {
    href: PropTypes.string.isRequired,
    children: PropTypes.string.isRequired
};

export default MenuItem;

Picture: Shows you error in console if the expected type is different

Picture: Shows you error in console if the expected type is different

Cumulonimbus answered 1/11, 2019 at 11:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.