Ivan Semochkin
Ivan Semochkin

Reputation: 8897

I don't understand how components are mounted

I use pattern container/representational components.
I have CardContainer component which fetch data from a server and pass it to a Card component
Container component:

class CardContainer extends Component {
    state = {
        'card': null
    }
    componentDidMount() {
        fetch(`${BASEURL}/api/cards/${this.props.params._id}/`)
            .then(res => res.json())
            .then(card => this.setState({'card': card}))
   }

    render() {
        return <CardDetail card={this.state.card} />
   }

Representational component:

class CardDetail extends Component {
    render() {
        return (
            <div>
                {this.props.card._id}
            </div>
        )
    }
}

In that case I have an error:

Uncaught TypeError: Cannot read property '_id' of null

So render method of a child called before componentDidMount of a parrent.
But in the case when I pass stateless function component to a child all works fine:

const FunctionChild = props => <h1>{props._id}</h1>

class CardDetail extends Component {
    render() {
        return (
            <div>
                <FunctionChild {...this.props.card} />
            </div>
        )
    }
}

I use console.log in components render and in a componentDidMount method to understand the method resolution:

  1. Mount container
  2. Mount child
  3. Mount function child
  4. DidMount container method

So componentDidMount still called last but all works fine. Please someone explain what am I missing.

Upvotes: 1

Views: 72

Answers (1)

Mayank Shukla
Mayank Shukla

Reputation: 104459

Reason is, initially you defined the card value as null, and accessing the value of id, that's why it is throwing the error :

can not access property id of null

Because you are fetching the data from api, it is asynchronous call and will take time to return the data, until you didn't get the data, value of card will be null.

One way of fixing this is, initialise the card with {} instead of null, like this:

class CardContainer extends Component {
    state = {
        'card': {}  //change this
    }
    componentDidMount() {
        fetch(`${BASEURL}/api/cards/${this.props.params._id}/`)
            .then(res => res.json())
            .then(card => this.setState({'card': card}))
   }

    render() {
        return <CardDetail card={this.state.card} />
   }

Or put the check inside the child component before accessing the id value, like this:

class CardDetail extends Component {
    render() {
        return (
            <div>
                {this.props.card && this.props.card._id}
            </div>
        )
    }
}

Upvotes: 3

Related Questions