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>${i}</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:tim.smith.hdg#gmail.com">
<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>
);
}
}
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>${i}</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:tim.smith.hdg#gmail.com">
<FontAwesomeIcon icon="at" /> {this.props.data.email}
</a>
</div>
<nav id="nav" className='nav'>
<ul>
{
//nav
}
</ul>
</nav>
</div>
);
}
}
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>${i}</li>")
}
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>${i}</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:tim.smith.hdg#gmail.com"><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>${i}</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:tim.smith.hdg#gmail.com"><FontAwesomeIcon icon="at" /> {this.props.data.email}</a>
</div>
<nav id="nav" className='nav'>
<ul>
{
//nav
}
</ul>
</nav>
</div>
);
}
}
Related
I am a beginner in using the React JS framework. Based on the official React JS documentation, an example is given for changing the state of a component that has a connected hierarchy. But in my case this time I split the components for Header and Main separately.
index.js
ReactDOM.render(
<React.StrictMode>
<Header />
<Main />
</React.StrictMode>,
document.getElementById('root')
);
In the Header component I also have another sub component that functions to activate / deactivate the sidebar which is also a sub menu for the Main component.
Header.js
import { BtnSidebarOnClick } from './Sidebar';
const Header = () => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebarOnClick />
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
export default Header;
Main.js
import { Sidebar } from './Sidebar';
const Main = () => {
return (
<main className="main">
<Sidebar />
<div className="main__content">
...
</div>
</main>
);
}
export default Main;
Notice that the BtnSidebarOnClick and Sidebar components are not connected. In my case, this time I want to make the Sidebar component accept state to detect whether the button contained in the BtnSidebarOnClick component is clicked / not.
Sidebar.js
class BtnSidebarOnClick extends React.Component {
constructor(props) {
super(props);
this.state = { onClick: false };
}
handleClick() {
this.setState(state => ({ onClick: !state.onClick }));
}
render() {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={this.handleClick.bind(this)}>
<i className="material-icons">menu</i>
</div>
</div>
);
}
}
const Sidebar = () => {
return (
<div className="main__sidebar"> {/* set style if BtnSidebarOnClick clicked */}
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}
export { Sidebar, BtnSidebarOnClick };
So how do you set these two components to receive the same state?
TLDR; You should pull out the button state into the parent and pass it into the children component.
By the way, it is a common way to have file App.js for your main Application file. In your case, it should be like this:
index.js
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = { isClicked: false };
}
handleClick() {
this.setState(state => ({ isClicked: !state.isClicked }));
}
render() {
return (
<div>
<Header onClick={this.handleClick} /> // --> notice
<Main isClicked={this.state.isClicked} /> // --> notice
</div>
)
}
}
Header.js
import BtnSidebar from './BtnSidebar';
const Header = (props) => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebar onClick={props.onClick} /> // --> notice
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
Main.js
import Sidebar from './Sidebar';
const Main = (props) => {
return (
<main className="main">
<Sidebar isClicked={props.isClicked} /> // --> notice
<div className="main__content">
...
</div>
</main>
);
}
BtnSidebar.js
const BtnSidebar = (props) => {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={props.onClick} // --> notice
>
<i className="material-icons">menu</i>
</div>
</div>
);
}
Sidebar.js
const Sidebar = (props) => {
return (
<div
className={
props.isClicked ? 'main__sidebar-clicked' : 'main__sidebar' // --> notice
}
>
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}
class Navbar extends Component {
state = {};
getLink = (e) => {
const select = document.getElementById("drpdwn");
console.log(select.value);
};
render() {
return (
<>
<div className="container">
<nav className="navbar navbar-dark bg-primary">
<div
id="drpdwn"
onClick={() => this.getLink()}
className="dropdown"
>
<span className="dropbtn">Dropdown</span>
<div className="dropdown-content">
<a value="link1" href="#">
Link 1
</a>
<a value="link2" href="#">
Link 2
</a>
<a value="link3" href="#">
Link 3
</a>
</div>
</div>
</nav>
</div>
</>
);
}
}
export default Navbar;
All I want is whenever a user selects any link such as Link 1,2 or 3 I just want to get the value as simple as that. but all I'm getting is undefined. any suggestions??
You can change the code like this and use tags instead of and then tags.
import React, { Component } from "react";
class Navbar extends Component {
state = {};
getLink = (e) => {
const changeValue = e.target.value
console.log(changeValue )
};
render() {
return (
<>
<div className="container">
<nav className="navbar navbar-dark bg-primary">
<select
id="drpdwn"
onChange={(e) => this.getLink(e)}
className="dropdown"
>
<option value="link1" href="#">
Link 1
</option>
<option value="link2" href="#">
Link 2
</option>
<option value="link3" href="#">
Link 3
</option>
</select>
</nav>
</div>
</>
);
}
}
export default Navbar;
First, remove the anonymous function inside your onClick, like this :
onClick={this.getLink}
This will automatically pass the event into your function, so (e) will be defined. This is the equivalent of doing :
onClick={(e) => this.getLink(e)}
Once you've done that, you must recover the value inside the event in your function.
getLink = (e) => {
const value = e.target.value;
console.log(value);
};
I notice you want to create a navbar with a dropdown list
class Navbar extends Component {
render() {
return (
<>
<div className="container">
<nav className="navbar navbar-dark bg-primary">
<div
id="drpdwn"
onClickCapture={(e) => {
e.preventDefault();
// e.target which is the target you click within this div
console.log(e.target);
}}
className="dropdown"
>
<span className="dropbtn">Dropdown</span>
<div className="dropdown-content">
<a href="/">
Link 1
</a>
<a href="/">
Link 2
</a>
<a href="/">
Link 3
</a>
</div>
</div>
</nav>
</div>
</>
);
}
}
This is how you can do it.
import React, { Component } from "react";
class Navbar extends Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
getLink = ({ target }) => {
if (target.tagName.toLowerCase() === "a") {
console.log(target.getAttribute("value"));
}
};
render() {
return (
<>
<div className="container">
<nav className="navbar navbar-dark bg-primary">
<div id="drpdwn" onClick={this.getLink} className="dropdown">
<span className="dropbtn">Dropdown</span>
<div className="dropdown-content">
<a value="link1" href="#">
Link 1
</a>
<a value="link2" href="#">
Link 2
</a>
<a value="link3" href="#">
Link 3
</a>
</div>
</div>
</nav>
</div>
</>
);
}
}
export default Navbar;
You may try the demo in codesandbox
https://codesandbox.io/s/gettingthevalue-yeqoi
im sending a li item from currencyDropdown.js to panelDropdown.js component but it's returning that prop is not a function
currencyDropdown.js
method
setCurrency = (currency) => {
this.props.dropCurrency(currency);
}
<div className="all_currrency">
<ul>
{
this.props.currencies.map((currency) => (
<li key={currency.name} onClick={() => {
this.setCurrency(currency)
}}>
<div className="currenct_item">
<div className="currency_flag">
<img src={currency.flag} alt=""/>
</div>
<div className="currency_name">
<span>{currency.name}</span>
</div>
</div>
</li>
))
} </ul>
</div>
panelDropdown.js
method
setCurrency = (currency) => {
this.setState({currentCurr: currency.name, currentFlag: currency.flag});
this.props.removeEl(currency); // this is giving the error removeEl is not a function
}
<CurrencyItems dropCurrency={this.setCurrency} currencies={this.props.all_currencies}/>
I have a set of items that needs to be shown in the UI, like a header and list of items under it. There is a parent component where I am passing this data to a file that is shown below. Based on this the parent-child layout is shown. Now I need to expand/collapse based on the click of the header.
There is a class "open" and "close " that can be attached to the div. Based on it the it gets collapse/expands. The point is how do it item wise
Can someone help
import React from "react";
import Child from "./Child";
import Parent from "./Parent";
export default class Helper extends React.Component{
constructor(props: any) {
super(props);
this.state = {
parent:{},
children:{},
};
}
componentDidMount() {
this.setParentValue();
this.setChildValue();
}
render() {
const { parent, children } = this.state;
const { name } = this.props;
return (
<>
<div className="an-panel expand-panel expand-close">
<div className="an-panel-header">
<div className="title-holder">
<span className="toggle-icon far fa-plus-square" />
<span className="toggle-icon far fa-minus-square" />
<h5>{name}</h5>
</div>
<div className="action-holder">
<div className="status-holder">
<Parent
parent = {parent}
onSelect={this.handleParentClick}
/>
</div>
</div>
</div>
{children.map(({ id, name },id) => (
<div className="an-panel-body" key={id}>
<ul className="applications-list-holder">
<li>
<div className="name">{name}</div>
<div className="status">
<Child
children={children}
onSelect={this.setChildSwitchValue}
/>
</div>
</li>
</ul>
</div>
))}
</div>
</>
);
}
}
Ok let me explain it to you, here is your code
import React from "react";
import Child from "./Child";
import Parent from "./Parent";
export default class Helper extends React.Component{
constructor(props: any) {
super(props);
this.state = {
parent:{},
children:{},
navBarStatus: false,
};
}
componentDidMount() {
this.setParentValue();
this.setChildValue();
}
changeNavBar = (e, status)=>{
this.setState({navBarStatus: !status});
}
render() {
const { parent, children } = this.state;
const { name } = this.props;
return (
<>
<div className={`an-panel expand-panel ${this.state.navBarStatus ? "expand-open" : "expand-close"}`}>
<div className="an-panel-header" onClick={(e)=>this.changeNavBar(e, this.state.navBarStatus)}>
<div className="title-holder">
<span className="toggle-icon far fa-plus-square" />
<span className="toggle-icon far fa-minus-square" />
<h5>{name}</h5>
</div>
<div className="action-holder">
<div className="status-holder">
<Parent
parent = {parent}
onSelect={this.handleParentClick}
/>
</div>
</div>
</div>
{children.map(({ id, name },id) => (
<div className="an-panel-body" key={id}>
<ul className="applications-list-holder">
<li>
<div className="name">{name}</div>
<div className="status">
<ChildSetting
children={children}
onSelect={this.setChildSwitchValue}
/>
</div>
</li>
</ul>
</div>
))}
</div>
</>
);
}
}
You can see I have taken a new property in state navBarStatus. Based on navBarStatus value I am changing CSS class which will expand/close your attached div
I am trying to pass data from child to parent state using
I have to state ProjectList and Header, the header(website header) state is the child compenent of parent, there links on header,if it clicks the link then I am getting an id through that link and then passing that id to through redirect.
but it gives me error maximum dept exceeded.The id I want to passs from header to project list is also coming from other state which is not the child of project list.
ProjectList State:
class ProjectList extends React.Component{
constructor(props){
super(props);
this.showDetail=this.showDetail.bind(this);
this.state={
category_projects:[],
projectPage:false,
clk_project:null,
category:[]
}
}
componentDidMount() {
fetch(`http://127.0.0.1:8000/portfolio/get_category_project/`+this.props.match.params.id+'/').then(response => response.json())
.then(projectsJson => this.setState({category_projects: projectsJson},()=>{
this.sortProjects()
}))
fetch('http://127.0.0.1:8000/portfolio/create_category/')
.then(response=>response.json())
.then(categoryJson => this.setState({category: categoryJson},()=>{
this.sortCategory()
}))
}
sortCategory = ()=>{
let sortArray=this.state.category;
let swap;
for (let i=0;i<sortArray.length;i++)
{
for (let j=0;j<sortArray.length;j++){
if(sortArray[i].category_rank>sortArray[j].category_rank){
swap=sortArray[i];
sortArray[i]= sortArray[j];
sortArray[j]=swap;
}
}
}
this.setState({category:sortArray})
};
showDetail=(evt)=>{
evt.preventDefault();
this.setState({projectPage:true});
this.setState({clk_project:JSON.parse(evt.currentTarget.getAttribute('data-item'))});
};
sortProjects=()=>{
let sortArray=this.state.category_projects;
let swap;
for (let i=0;i<sortArray.length;i++)
{
for (let j=0;j<sortArray.length;j++){
if(sortArray[i].project_rank>sortArray[j].project_rank){
swap=sortArray[i];
sortArray[i]= sortArray[j];
sortArray[j]=swap;
}
}
}
this.setState({category_projects:sortArray})
};
render() {
let redirect=null;
if (this.state.projectPage){
redirect=<Redirect to={`/project/${this.state.clk_project.id}`}/>
}
return(
<div>
{redirect}
<div className="site_wrapper">
<div className="clearfix">
</div>
<Header category={this.state.category}/>
<div className="clearfix">
</div>
<div className="page_title2">
<div className="container">
<div className="title"><h2>Projects</h2></div>
</div>
</div>
<div className="clearfix">
</div>
<div className="container">
<br />
{this.state.category_projects.map(proj=>{
return(
<div className="column">
<img data-item={JSON.stringify(proj)} onClick={this.showDetail} src={"http://127.0.0.1:8000"+proj.file} alt="Snow" className="ImgWidth"/>
</div>
)
})}
</div>
<div className="clearfix margin_top5">
</div>
<div className="footer_graph">
</div>
<Footer projects={this.state.category_projects} id={this.props.match.params.id}/>
</div>
</div>
)
}
}
Header State:
class Header extends React.Component{
constructor(props){
super(props);
this.showCatDetail=this.showCatDetail.bind(this);
this.state={
category:[],
clk_category:[],
detail:false
}
}
showCatDetail=(e)=>{
e.preventDefault();
this.setState({detail:true,clk_category:JSON.parse(e.currentTarget.getAttribute('data-item'))},()=>{
console.log(this.state.clk_category)
});
};
render(){
let hreflink=null;
let redirect=null;
if (this.state.detail){
redirect=<Redirect to={`/category_project/${this.state.clk_category.id}`}/>
}
return(
<div>
{redirect}
<header id="header">
<div id="trueHeader">
<div className="wrapper">
<div className="container">
<div className="logo">
<a href={hreflink} id="logo">
</a>
</div>
<div className="menu_main">
<div className="navbar yamm navbar-default">
<div className="container">
<div className="navbar-header">
<div className="visibledevice">
<ul className="nav navbar-nav">
<li><a href={hreflink} className="active">
<i className="fa fa-home">
</i> Home</a></li>
</ul>
</div>
</div>
<div className="navbar-collapse collapse pull-right">
<ul className="nav navbar-nav">
<li><a href={hreflink} className="active">
<i className="fa fa-home">
</i> Contact Us</a></li>
{this.props.category.map(cat=>{
return(
<li data-item={JSON.stringify(cat)} onClick={(e)=>this.showCatDetail(e)}><a href={hreflink} >
<i className="fa fa-home">
</i>{cat.category_name}</a></li>
)
})}
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</div>
)
}
}
this.props.match.params.id this id I want to get from header as I trying to pass from in header.The this.props.match.params.id in project list can come from Header or other state.Please need suggestion how to pass this id from header to project list as well as the id can comes from other state to project.