I have a react application and I'm trying to build a Navbar component using data from a javascript file.
My NavbarData.js file looks like the following:
const NavbarData = [
{
id: 1,
text: "Typography"
},
{
id: 2,
text: "Buttons"
},
{
id: 3,
text: "icons"
}
]
export default NavbarData
I'm using .map()
to iterate over this data and create NavbarItem
components inside my App.js file.
// Build navmenu items
const navbarItems = this.state.navbarData.map(function(item){
return <NavbarItem key={item.id} text={item.text} id={item.id}></NavbarItem>
});
And here is my NavbarItem.js file
import React, { Component } from 'react';
class NavbarItem extends Component{
render(){
return(
<>
<li key={this.props.id} id={this.props.id}>{this.props.text}</li>
</>
)
}
}
export default NavbarItem
All of this gives me something that looks like this. Which is great.
But I want to add a click listener to each of these. As this is a single page application, I would like to render either a typography, buttons, or icons component. To do this, I need a function that will update the state of the parent component which in my case is just App.js
So I put the following function inside App.js
//This function changes the state so that different components can render
navClick(id) {
console.log('changed', id);
}
And I made sure to bind it in my constructor of App.js
this.navClick = this.navClick.bind(this);
My entire App.js file now looks like this
//React stuff
import React, { Component } from 'react';
//Bootstrap stuff
import { Container, Row, Col } from 'reactstrap';
//Layout
import NavbarItem from './layout/NavbarItem'
import NavbarData from './layout/NavbarData'
//Components
import Typography from './components/Typography/Typography'
import Buttons from './components/Buttons/Buttons'
//Styles
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
class App extends Component {
constructor(){
super();
// State determines what component is active and loads navbar data
this.state = {
navbarData: NavbarData,
typography: true,
buttons: false,
icons: false
}
this.navClick = this.navClick.bind(this);
}
//This function changes the state so that different components can render
navClick(id) {
console.log('changed', id);
}
render() {
// Build navmenu items
const navbarItems = this.state.navbarData.map(function(item){
return <NavbarItem key={item.id} text={item.text} id={item.id}></NavbarItem>
});
// Determine what component to display in main area using state
let elementToDisplay;
if(this.state.typography){
elementToDisplay = <Typography></Typography>
}
else if(this.state.buttons){
elementToDisplay = <Buttons></Buttons>
}
////////////////////////////////////////////////////
return (
<Container fluid={true}>
<Row>
<Col>Header</Col>
</Row>
<Row>
<Col xs="12" sm="12" md="1" lg="1" xl="1">
<ul>
{navbarItems}
</ul>
</Col>
<Col xs="12" sm="12" md="11" lg="11" xl="11">
{elementToDisplay}
</Col>
</Row>
<Row>
<Col>Footer</Col>
</Row>
</Container>
);
}
}
export default App;
The problem comes when I try to attach the navClick function to the mapped NavbarItem like so.
// Build navmenu items
const navbarItems = this.state.navbarData.map(function(item){
return <NavbarItem navigationWhenClicked={this.navClick} key={item.id} text={item.text} id={item.id}></NavbarItem>
});
The error I receive is the following:
TypeError: this is undefined
When googleing this issue, this is the top post. React: "this" is undefined inside a component function
But that's not my problem as I am making sure to bind my function.
I really have no idea what I'm doing wrong here. Any help would be appreciated.