React HoC PropTypes/PascalCase

In React, I am attempting to pass a component as a prop to another component (HoC). But when I try to use that component within, I two errors and the component won't render.

Here are the errors:

"Warning: propTypes was defined as an instance property on Gridlist. Use a static property to define propTypes instead."

"Warning: is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements."

Here's the code:

import React, {Component} from 'react'
import ActivityCard from '../activities-card/activities-card'
import Gridlist from '../grid-list/grid-list'
import PropTypes from 'prop-types'

export default class ActivitiesGrid extends Component {
  propTypes = {
    items: PropTypes.array,
    cellHeight: PropTypes.number,
    columns: PropTypes.number
  }
  render () {
    return (
      <Gridlist
        items={this.props.items}
        cardComponent={<ActivityCard />}
        cellHeight={this.props.cellHeight}
        columns={this.props.columns}/>
    )
  }
}

import ActivityCard from '../activities-card/activities-card'
import GridListTile from '@material-ui/core/GridListTile'
import MaterialGridList from '@material-ui/core/GridList'
import PropTypes from 'prop-types'
import React from 'react'

export default class Gridlist extends React.Component {
  propTypes = {
    items: PropTypes.array,
    cellHeight: PropTypes.number,
    columns: PropTypes.number,
    image: PropTypes.string,
    cardComponent: PropTypes.instanceOf(ActivityCard)
  }
  render () {
    return (
      <MaterialGridList cellHeight={this.props.cellHeight} cols={this.props.columns}>
        {this.props.items.map(item => (
          <GridListTile cols={1} key={item.key}>
            <cardComponent content={item} />
          </GridListTile>
        )
        )}
      </MaterialGridList>
    )
  }
}

import Button from 'glamorous'
import Card from '@material-ui/core/Card'
import CardActions from '@material-ui/core/CardActions'
import CardContent from '@material-ui/core/CardContent'
import PropTypes from 'prop-types'
import React from 'react'
import Typography from '@material-ui/core/Typography'

export default class ActivityCard extends React.Component {
  propTypes = {
    content: PropTypes.object
  }
  render () {
    return (
      <Card>
        <CardContent>
          <img src={this.props.content.imageUri} />
          <Typography>{this.props.content.title}</Typography>
        </CardContent>
        <CardActions>
          <Button>Points</Button>
        </CardActions>
      </Card>
    )
  }
}

1 answer

  • answered 2018-10-09 16:26 Ben Swinburne

    You need to define propTypes as a static property so it should be

    static propTypes = {
    }
    

    Static properties is still a stage 2 proposal (info) so you'll need to use a Babel plugin to transform the syntax. The transform-class-properties plugin will handle this syntax for you.

    Obviously you could just define the property on the class instead if you didn't want to use the babel plugin. This is essentially what the plugin would transform your code into.

    export default class ActivityCard extends React.Component {
      ...
    }
    
    ActivityCard.propTypes = {
      items: PropTypes.array,
      cellHeight: PropTypes.number,
      columns: PropTypes.number
    }
    

    The react docs actually explain this in the documentation for defaultProps but the principle is the same.

    https://reactjs.org/docs/typechecking-with-proptypes.html#default-prop-values

    If you are using a Babel transform like transform-class-properties , you can also declare defaultProps as static property within a React component class. This syntax has not yet been finalized though and will require a compilation step to work within a browser. For more information, see the class fields proposal.

    To sort the pascal case error, you'd need to do one of the following

    Clone the element and pass a new prop to it

    {React.cloneElement(this.props.cardComponent, content={item})}
    

    Name it pascal case. (not sure if you can do this on the prop itself).

    // store it in some variable
    const CardComponent = this.props.cardComponent;
    
    // n.b. you might not need to do this if you just pass the prop in with
    // the correct casing although i'm not sure this is a sensible approach.
    // I think you're saying PascalCasing the prop directly doesn't work?
    
    <CardComponent content={item} />