ComponentA has ComponentB as a child, in an arrangement like

const ComponentA = (props) => (
  <ComponentB {...props} />
)

The propTypes for ComponentA should contain every variable that is directly referenced by ComponentA, including if that variable is passed via an enumerated prop to ComponentB.

// Good!
const ComponentA = ({ hidden, style }) => (
  <div style={style}>
    <ComponentB hidden={hidden} />
  </div>
)
ComponentA.propTypes = {
  hidden: PropTypes.bool,
  style: PropTypes.object,
}

The propTypes for ComponentA should not contain any props that are passed to and used by the child via prop spread.

// Good!
const ComponentA = (props) => (
  <ComponentB {...props} />
)
ComponentA.propTypes = {}

By default, if your component accepts a prop, it should be required. If a prop is not required, it should be given a default value using defaultProps, never a parameter destructuring default value. Never should a prop be both unrequired and not given a default value. This is a good practice even if the defaultValue is null; it conveys intention at the component’s API boundary that someone took the time to make sure a null value works with the component.

// Good!
const ComponentA = ({ hidden, style }) => (
  <ComponentB hidden={hidden} />
)
ComponentA.propTypes = {
  hidden: PropTypes.bool,
  style: PropTypes.object.isRequired,
}
ComponentA.defaultProps = {
  hidden: false,
}

Passing objects into container components is perfectly acceptable, as long as that object has some defined meaning elsewhere in your application. For example: a database record. The PropType definition for this object should be stored in a central location shared between many components.

// Good!
import { UserRecord } from 'app/proptypes/users'
const ContainerA = ({ user }) => (
  <ContainerB user={user} />
)
ContainerA.propTypes = {
  user: UserRecord,
}

Never assign meaning to objects inside the component tree. In other words, do not spontaneously create an object just for the sake of only having to pass one prop. In other words, if you’re writing PropTypes.shape() inside the same file as any component, you’re doing something wrong.

// Bad!
const ContainerA = ({ clickedAt, clickedBy }) => (
  <ContainerB clicked={ { at: clickedAt, by: clickedBy } } />
)

Never pass objects into presentational components. Destructure the object into the primitive types that are necessary to render the child component. There is one exception to this rule: React style objects are acceptable to pass to presentational components.

// Good!
const ContainerA = ({ style, user }) => (
  <PresentationB age={user.age} name={user.name} style={style} />
)
ContainerA.propTypes = {
  style: PropTypes.object.isRequired,
  user: UserRecord,
}
PresentationB.propTypes = {
  age: PropTypes.number.isRequired,
  name: PropTypes.string.isRequired,
  style: PropTypes.object.isRequired,
}

If you are passing a Date as a prop, all of the above rules apply. Passing Date objects is fine between containers, but pre-serialize the Date into a string before passing it to a presentational component.

const ContainerA = ({ clickedAt }) => (
  <PresentationB clickedAt={moment(clickedAt).format('MM DD')} />
)
ContainerA.propTypes = {
  clickedAt: PropTypes.instanceOf(Date).isRequired,
}
PresentationB.propTypes = {
  clickedAt: PropTypes.string.isRequired,
}