Joris
Joris

Reputation: 2796

React - prop-types is marked as required

I'm having an error when I'm using propTypes and conditional rendering children.

The error : Warning: Failed prop type: The prop 'rate' is marked as required in 'Rate', but its value is 'undefined'

// index.js
import React, { Component } from "react";
import { render } from "react-dom";
import Rate from "./components/Rate";
import Card from "./components/Card";

class App extends Component {
  state = {
    loading: true
  };

  componentDidMount() {
    setTimeout(() => this.setState({ loading: false, rate: 5 }), 5000);
  }

  render() {
    return (
      <Card loading={this.state.loading}>
        <Rate rate={this.state.rate} />
      </Card>
    );
  }
}

render(<App />, document.getElementById("root"));

// rate.js
import React from "react";
import PropTypes from "prop-types";

const Rate = ({ rate }) => (
  <div>
    <span>{rate}/10</span>
  </div>
);

Rate.propTypes = {
  rate: PropTypes.number.isRequired
};

export default Rate;

I have reproduced my error here : https://codesandbox.io/embed/qvx8q78ypw

I want to render children element only if loading is set to false.
EDIT : I don't understand why I get this error on Rate component until it is rendered
I don't understand why I get this error.

Thanks for help

Upvotes: 1

Views: 2676

Answers (3)

devserkan
devserkan

Reputation: 17608

You can use a default prop value for the time being:

Rate.defaultProps = {
  rate: 0
};

You are not rendering Card conditionally as @Cody Moniz told in the answer. You are always rendering it. Probably in your Card component you are hiding or showing something depends on the loading prop that you passed to it. Do not bother like that, just use conditional rendering properly and clean up your Card component a little bit maybe.

render() {
    return !this.state.loading && (
      <Card>
        <Rate rate={this.state.rate} />
      </Card>
    );
}

Here, if React gets null or false it renders nothing. So, when your loading state is false you render nothing, after it changes to true then the second part evaluated and renders your component. Second part is not evaluated until the first one is true with logical && operator. This is a nice and clean way to do conditional rendering especially you want to show nothing in the first condition.

If you want to show something about Card (means render it) but only render Rate component conditionally, you can apply this logic in your Card component with loading prop that your Card component gets.

Upvotes: 2

Cody Moniz
Cody Moniz

Reputation: 5065

<Rate> is still being rendered in <App>, the only thing <Card> does is hide or show the already-rendered this.props.children element(s) passed to it.

Upvotes: 1

imjared
imjared

Reputation: 20554

You're trying to load the <Rate /> component with a rate prop equal to this.state.rate in <App />. Your App component state has nothing set for this.state.rate so while <Rate /> expects PropTypes.Number, it's actually getting undefined. That's why it's complaining.

To solve, you could set the state for <App /> to something like:

state = {
  loading: true
  rate: 0,
};

Upvotes: 0

Related Questions