简体   繁体   中英

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.

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>
        )
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM