Reputation: 1417
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:
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
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
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
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