1

I am trying to figure out how to pass props to a component matching a certain route. As you can see from the code below, every time a user hits /items/:id I get the id from the url and hopefully get the right item accessing the array using that id.

<Route exact path="/items/:id" render={(props) => {
    let itemId = parseInt(props.location.pathname.replace('/items/', ''));
       console.log(this.state.items[itemId].description);
       return <ItemDetail item={this.state.items[itemId]} />;

The problem is if I run this code, React will crash returning an angry red cannot read property description of undefined, but — and that's where I get totally confused — if I try to log this.state.items[itemId] I get to see the full object in the console. Naturally the exact same thing happens if I pass item as a prop and try to get the description out of it from the child component ItemDetail.

You need to now I am currently setting the data in the componentDidMount lifecycle method with a simple

 componentDidMount() {
    this.setState({
      items: data
    });
  }

and React breaks but if I change that to the soon to be deprecated componentWillMount the application runs as it should and I don't understand why. If state has been set after mounting and I know for a fact that the item exists in the array because I can log it, why can't I read the properties inside it?

haunted85
  • 1,501
  • 5
  • 21
  • 40

1 Answers1

0

Since seem to be synchronously setting state in componentDidMount, you can safely move it to constructor to avoid the error

constructor(props) {
    super(props);
    this.state = {
         items: data
     }
}

If the data is coming from an API call then you would need to make that API call in componentDidMount and set the state on onsuccess or onerror callback and provide a conditional check when using the state value in render like

return <ItemDetail item={this.state.items? this.state.items[itemId] : {}} />;

or show loading till the API call is a success

render() {
   if(!this.state.items) {
       return <div>Loading...</div>
   }
   return <Route exact path="/items/:id" render={(props) => {
    let itemId = parseInt(props.location.pathname.replace('/items/', ''));
       console.log(this.state.items[itemId].description);
       return <ItemDetail item={this.state.items[itemId]} />;
}
Shubham Khatri
  • 211,155
  • 45
  • 305
  • 318
  • @haunted85 You need to handle the case where there's no data to display. Show a "Loading..." message or something similar while your component waits for the data. – Ross Allen Jul 10 '18 at 08:12
  • @RossAllen there are multiple ways to handle it, it depends on OP use case. OP can either show a loading in the parent or in ItemDetails. But sure a way to handle it is loading – Shubham Khatri Jul 10 '18 at 08:13
  • @ShubhamKhatri thank you for your solution! But still I was wondering, how come I can read an object from state just fine but I am not able to access its properties? – haunted85 Jul 10 '18 at 08:35
  • That is because this.state is defined and hence you can read `this.state.items` which gives you undefined, and now when you try, `this.state.items[itemId]` its essentially `undefined[itemId]` which isn't valid and hence you get an error – Shubham Khatri Jul 10 '18 at 09:14