Tithos
Tithos

Reputation: 1417

React seeing all but on array

I have a child component that sees all the props but one array that I am trying to map over.

react dev tool sees the array:

enter image description here

but I get "Cannot read property 'map' of undefined"

class Header extends Component {
  render() {
    const nav = this.props.data.nav.map(i => `<li><a href="#${i}">${i}</a></li>`)
    return (
      <div className="header">
        <div className="left">
          <h1>{this.props.data.name}</h1>
          <p>{this.props.data.tag}</p>
        </div>
        <div className="right">
          <h2>{this.props.data.t1}</h2>
          <h3>{this.props.data.t2}</h3>
        </div>
        <div className="header-contact">
          <a rel="noopener" href="tel:+14156943568">
          <FontAwesomeIcon icon={faPhone} /> {this.props.data.phone} 
          </a>
          <a rel="noopener" href="mailto:[email protected]">
          <FontAwesomeIcon icon="at" /> {this.props.data.email}
          </a>
        </div>
        <nav id="nav" className='nav'>
          <ul>
            {
              //nav
            }
          </ul>
        </nav>
      </div>
    );
  }
}

I'm using the <Header /> component in the follow way:

class Resume extends Component {
  constructor(props) {
    super();
    this.state = { header: {}, skills: {} }
  }
  componentDidMount() {
    this.setState({
        ...data
    });
  }
  render() {
    return (
      <div className="resume">
        <Header data={this.state.header} />
        <Skills data={this.state.skills} />
      </div>
    );
  }
}

Upvotes: 0

Views: 59

Answers (3)

Dacre Denny
Dacre Denny

Reputation: 30360

You should check that props.data is defined, and that props.data.nav is an array, to address the errors.

The reason you need to perform this check is because the props.data.nav is not present during the first render() of the <Header /> component. This is because the nav data is only available after the componentDidMount() hook in <Resume /> is called (which happens after the first render of <Header />)

You could make the following adjustment to resolve this:

class Header extends Component {

  // [UPDATE] add helper method to simplify safer access to data.nav array
  getNavItems() {

      // If no data, return empty array
      if(!this.props.data) return [];

      // If nav not an array, return empty array
      if(!Array.isArray(this.props.data.nav)) revturn [];

      // Safely access/return nav array
      return this.props.data.nav;
  }

  render() {

    // [UPDATE] use local helper getNavItems method to safely access item array
    const nav = this.getNavItems().map(i => `<li><a href="#${i}">${i}</a></li>`)
    return (
      <div className="header">
        <div className="left">
          <h1>{this.props.data.name}</h1>
          <p>{this.props.data.tag}</p>
        </div>
        <div className="right">
          <h2>{this.props.data.t1}</h2>
          <h3>{this.props.data.t2}</h3>
        </div>
        <div className="header-contact">
          <a rel="noopener" href="tel:+14156943568">
          <FontAwesomeIcon icon={faPhone} /> {this.props.data.phone} 
          </a>
          <a rel="noopener" href="mailto:[email protected]">
          <FontAwesomeIcon icon="at" /> {this.props.data.email}
          </a>
        </div>
        <nav id="nav" className='nav'>
          <ul>
            {
              //nav
            }
          </ul>
        </nav>
      </div>
    );
  }
}

Upvotes: 1

EQuimper
EQuimper

Reputation: 5929

You get map is undefined cause you don't get an array. You can't map undefined. The way I will do this will be to get an empty array if I don't have the props yet. This way you can still map over it but receive no element

class Header extends Component {
  render() {
    const navData = this.props.data && this.props.data.nav || []
    const nav = navData.map(i => `<li><a href="#${i}">${i}</a></li>`)
    return (
      <div className="header">
        <div className="left">
          <h1>{this.props.data.name}</h1>
          <p>{this.props.data.tag}</p>
        </div>
        <div className="right">
          <h2>{this.props.data.t1}</h2>
          <h3>{this.props.data.t2}</h3>
        </div>
        <div className="header-contact">
          <a rel="noopener" href="tel:+14156943568"><FontAwesomeIcon icon={faPhone} /> {this.props.data.phone}</a>
          <a rel="noopener" href="mailto:[email protected]"><FontAwesomeIcon icon="at" /> {this.props.data.email}</a>
        </div>
        <nav id="nav" className='nav'>
          <ul>
            {
              //nav
            }
          </ul>
        </nav>
      </div>
    );
  }
}

Or you can handle this case from the parent also

class Parent extends Component {
  render() {
    return (
      <div>
        <Header data={this.state.header} nav={this.state.header && this.state.header.nav || []} /> 
      </div>
    )
  }
}


class Header extends Component {
  render() {
    const nav = this.props.nav.map(i => `<li><a href="#${i}">${i}</a></li>`)
    return (
      <div className="header">
        <div className="left">
          <h1>{this.props.data.name}</h1>
          <p>{this.props.data.tag}</p>
        </div>
        <div className="right">
          <h2>{this.props.data.t1}</h2>
          <h3>{this.props.data.t2}</h3>
        </div>
        <div className="header-contact">
          <a rel="noopener" href="tel:+14156943568"><FontAwesomeIcon icon={faPhone} /> {this.props.data.phone}</a>
          <a rel="noopener" href="mailto:[email protected]"><FontAwesomeIcon icon="at" /> {this.props.data.email}</a>
        </div>
        <nav id="nav" className='nav'>
          <ul>
            {
              //nav
            }
          </ul>
        </nav>
      </div>
    );
  }
}

Upvotes: 1

Pedro Sim&#227;o
Pedro Sim&#227;o

Reputation: 293

Can you show from where this.props.data.nav comes from? Maybe when the component mounts, this.props.data or this.props.data.nav is undefined. React dev tool shows the array because after the error, this.props.data.nav is already set and not undefined. Try something like:

var nav = null; if(this.props.data != undefined && this.props.data.nav != undefined){ nav = this.props.data.nav.map(i => "<li><a href="#${i}">${i}</a></li>") }

Upvotes: 1

Related Questions