Maximum update depth exceeded when trying to setState when user clicks - javascript

I am trying to When user clicks on list set a state
I have listing in li tag in a loop when user click one of li tag i want to update some state but react throws error.
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested
Here my main piece of code:
class Header extends React.Component{
constructor(props){
super(props);
this.showCatDetail=this.showCatDetail.bind(this);
this.state={
category:[],
clk_category:[],
detail:false
}
}
componentDidMount() {
fetch('http://127.0.0.1:8000/portfolio/create_category/')
.then(response=>response.json())
.then(categoryJson => this.setState({category: categoryJson},()=>{
this.sortCategory()
}))
}
showCatDetail=(e)=>{
e.preventDefault();
this.setState({detail:true,clk_category:JSON.parse(e.currentTarget.getAttribute('data-item'))},()=>{
console.log(this.state.clk_category)
});
};
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})
};
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.state.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>
)
}
}
I can't figure out what the problem is...

onClick={(e)=>{this.showCatDetail(e)}}
Try this in onclick

Related

Why does adding useState result in a white screen in my react app?

The code below works completely fine and results in the image below.
import React from "react";
import "./App.css";
import { useState } from "react";
function App(){
return(
<body>
<div className="nav_bar">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"></link>
<ul className="top">
<div className="circle circle1">
<li className="material-icons noselect">
drag_handle
</li>
</div>
<div className="circle circle2">
<li className="material-icons noselect">
home
</li>
</div>
<div className="circle circle3">
<li className="material-icons noselect">
person_outline
</li>
</div>
</ul>
<nav>
<ul className="bottom">
<li className="material-icons noselect" id="feed-bottom">
drag_handle
</li>
<li className="material-icons noselect" id="home-bottom">
home
</li>
<li className="material-icons noselect" id="profile-bottom">
person_outline
</li>
</ul>
</nav>
</div>
</body>
);
}
export default App;
Result
Adding useState to get and set the current state causes the navbar to disappear and show a completely white screen. Specifically I am using useState to change the icon shown in the nav bar to text and to set the currernt state to the icon that is clicked. Code Below
import React from "react";
import "./App.css";
import { useState } from "react";
function App(){
const[selected, setselected] = useState('home');
if(selected === 'feed'){
const feed = document.getElementById('feed-bottom');
feed.innerHTML = 'FEED';
} else if (selected === 'profile') {
const profile = document.getElementById('profile-bottom');
profile.innerHTML = 'PROFILE';
}else{
const home = document.getElementById('home-bottom');
home.innerHTML = 'HOME';
}
return(
<body>
<div className="nav_bar">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"></link>
<ul className="top">
<div className="circle circle1">
<li className="material-icons noselect">
drag_handle
</li>
</div>
<div className="circle circle2">
<li className="material-icons noselect">
home
</li>
</div>
<div className="circle circle3">
<li className="material-icons noselect">
person_outline
</li>
</div>
</ul>
<nav>
<ul className="bottom">
<li className="material-icons noselect" id="feed-bottom" onClick={setselected('profile')}>
drag_handle
</li>
<li className="material-icons noselect" id="home-bottom" onClick={setselected('home')}>
home
</li>
<li className="material-icons noselect" id="profile-bottom" onClick={setselected('profile')}>
person_outline
</li>
</ul>
</nav>
</div>
</body>
);
}
export default App;
I've looked up many post that refernece similar issues but couldn't find one that pretained to mine. I would grealty appreciate some assitance.
This line
home.innerHTML = 'HOME';
will cause an error on mount, because on mount, the React elements returned from App haven't been returned yet - the container is still empty; the #feed-bottom element doesn't exist yet.
While you could fix it by only assigning if the element actually exists, it would be far better to do it the React way and put the values conditionally into the JSX. Don't use vanilla DOM methods in React unless you absolutely have to.
Another problem is that your listeners - eg onClick={setselected('home')} - run when computed (immediately), because you're invoking the setselected function instead of passing a function as a prop to onClick.
You also probably meant to pass feed in the feed-bottom element (instead of profile).
To implement your logic in the React way, you need something like:
<li className="material-icons noselect" id="feed-bottom" onClick={() => setselected('feed')}>
{ selected === 'feed' ? 'FEED' : 'drag_handle' }
</li>
<li className="material-icons noselect" id="home-bottom" onClick={() => setselected('home')}>
{ selected === 'home' ? 'HOME' : 'home' }
home
</li>
<li className="material-icons noselect" id="profile-bottom" onClick={() => setselected('profile')}>
{ selected === 'profile' ? 'PROFILE' : 'person_outline' }
person_outline
</li>
and remove all of the if(selected === 'feed'){ and the like code from the top.

Add class / remove class from li item in navbar (React)

I am trying to add 'active' class name to clicked navbar item dynamically and remove the 'active' class name from class respectively. I am using bootstrap so 'active' class name change the color of the li item.
class NavBar extends React.Component {
render() {
return (
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Blog - App</a>
</div>
<ul class="nav navbar-nav">
<li class="active">Home</li>
<li>Chat</li>
<li>About</li>
</ul>
</div>
</nav>
);
};
};
export default NavBar
I am new with React, do i need to handle onClick with a function? I will appreciate any idea or code suggestions.
In react you can do it like this:
By passing in a prop which determines the toggle of className.
const NavBar = props => {
return (
<nav className="navbar navbar-inverse">
<div class="container-fluid">
<div className="navbar-header">
<Link className="navbar-brand">Blog - App</Link>
</div>
<ul className="nav navbar-nav">
<li className={props.active ? 'active' : ''}><Link to="/main">Home</Link></li>
<li><Link to="/chat">Chat</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</div>
</nav>
);
};
export default NavBar
By State
But then in this case wouldn't be appropriate because nothing is going to click it.
import {useState} from 'react';
const NavBar = () => {
const [active, setActive] = useState('');
clickToChange () {
setActive(true);
}
render() {
return (
<nav className="navbar navbar-inverse">
<div class="container-fluid">
<div className="navbar-header">
<Link className="navbar-brand">Blog - App</Link>
</div>
<ul className="nav navbar-nav">
<li className={state.active ? 'active' : ''}><Link to="/main">Home</Link></li>
<li><Link to="/chat">Chat</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</div>
</nav>
);
};
};
export default NavBar
In your particular use case I'd probably suggest instead detecting the route
See example here:
React Router v4 - How to get current route?
yes, you could use onClick handler, but then again you will have to rehydrate it from the window.location;
class NavBar extends React.Component {
render() {
state = { active : null }
componentDidMount(){
/** you might as well check if location.pathname includes what you are looking
for before just setting it to state, assuming that there wont be anything
extra; skipping that for simplicity*/
this.setState(window.location.pathname);
}
handleClick = (activeLink) => {
this.setState(activeLink);
}
return (
<nav className="navbar navbar-inverse">
<div className="container-fluid">
<div className="navbar-header">
<a className="navbar-brand" href="#">Blog - App</a>
</div>
<ul className="nav navbar-nav">
<li onClick={()=>this.handleClick('/main')} className={`{this.state.active==='/main'? 'active':''}`}>Home</li>
</ul>
</div>
</nav>
);
};
};
export default NavBar
A more graceful implementation would be using a client side routing library like react-router which gives you Link components like NavLink which attaches active classnames automatically.

How do I update Angular styling using ElementRef?

I am new to Angular I am working on a bottom that updates the styling when pressed. Here is a copy of the HTML page.
<nav>
<button #toggleNavigation aria-expanded="false">
<mat-icon>menu</mat-icon>
</button>
<div class="sidebar-sticky collapse navbar-collapse">
<ul>
<side-menu-item *ngFor="let item of navItems" [hidden]="item.isHidden"
[finished]="item.finished" [name]="item.displayName"
[routeURL]="item.routeURL"
[ngClass]="{'mt-auto': item.name === 'Summary'}">
</side-menu-item>
</ul>
</div>
</nav>
When the button is pressed, this is what I would like it to update to.
<nav>
<button #toggleNavigation aria-expanded="true">
<mat-icon>menu</mat-icon>
</button>
<div class="sidebar-sticky collapse navbar-collapse show">
<ul>
<side-menu-item *ngFor="let item of navItems" [hidden]="item.isHidden" [finished]="item.finished" [name]="item.displayName" [routeURL]="item.routeURL" [ngClass]="{'mt-auto': item.name === 'Summary'}"></side-menu-item>
</ul>
</div>
</nav>
Clicking the button will display a navigation list. As you see in the code the styling is updated from class="sidebar-sticky collapse navbar-collapse" to class="sidebar-sticky collapse navbar-collapse show". Originally, I using a bootstrap.js file to handle this but it interfered with styling associated with another program. In my .ts file this is what I have
import { Component, ElementRef, Renderer2, ViewChild } from '#angular/core';
#Component({
selector: 'app-side-menu',
templateUrl: './side-menu.component.html',
styleUrls: ['./side-menu.component.scss']
})
export class NavMenuComponent {
#ViewChild('toggleNavigation', {static: false}) toggleNavigation: ElementRef;
constructor() {
onToggleNavigation() {
}
}
Any help would be greatly appreciated.
I would do this with [ngClass].
Try:
<nav>
<button #toggleNavigation aria-expanded="true" (click)="onToggleNavigation()">
<mat-icon>menu</mat-icon>
</button>
<div class="sidebar-sticky collapse" [ngClass]="{'show': !collapseSideNav}">
<ul>
<side-menu-item
*ngFor="let item of navItems"
[hidden]="item.isHidden"
[finished]="item.finished" [name]="item.displayName"
[routeURL]="item.routeURL"
[ngClass]="{'mt-auto': item.name === 'Summary'}"
(click)="collapseNavigation()"
>
</side-menu-item>
</ul>
</div>
</nav>
collapseSideNav: boolean = true; // can default it to whatever you like
onToggleNavigation() {
this.collapseSideNav = !this.collapseSideNav;
}
collapseNavigation() {
if (!this.collapseSideNav) {
this.collapseSideNav = true;
}
}
Use [ngClass] for this:
<nav>
<button [attr.aria-expanded]="collapsed" (click)="collapsed = !collapsed">
click
</button>
<div [ngClass]="{'show' : collapsed}" class="sidebar-sticky collapse navbar-collapse">
<ul>
content
</ul>
</div>
</nav>
https://stackblitz.com/edit/angular-3rn8no?file=src%2Fapp%2Fapp.component.ts

The onClick on the unodered list is not working?

The on onclick function which should call the function which is HTML inside Java-script (JSX). Does not seems to work??
Does onClick only works on a button or does it also work on the lists too?
class Top extends React.Component{
constructor(){
super();
this.searchjsx = this.searchjsx.bind(this);
}
searchjsx = () =>{
return(
<div id='searchdiv'>
<form id='searchform'>
<input type="text" id="input" name="search"></input>
</form>
</div>
);
}
render(){
return(
<div>
<div id="navbar">
<ul id="nav">
<li><a className="a" href='https://www.google.com/'>Home</a></li>
<li><a className="b" href='https://www.google.com/'>Profile</a></li>
<li><a className="c" href='https://www.google.com/'>Pricing</a></li>
<li onClick={this.searchjsx} id='sch'>Search..</li>
</ul>
</div>
<div>
</div>
</div>
);
}
}
The below worked for me:
import React from "react";
export default class Top extends React.Component {
state = {
showForm: false
};
searchjsx = () => {
console.log("Toggled showForm");
this.setState({ showForm: !this.state.showForm });
};
render() {
return (
<div>
<div id="navbar">
<ul id="nav">
<li>
<a className="a" href="https://www.google.com/">
Home
</a>
</li>
<li>
<a className="b" href="https://www.google.com/">
Profile
</a>
</li>
<li>
<a className="c" href="https://www.google.com/">
Pricing
</a>
</li>
<li onClick={this.searchjsx} id="sch">
Search..
</li>
{this.state.showForm ? (
<div id="searchdiv">
<form id="searchform">
<input type="text" id="input" name="search"></input>
</form>
</div>
) : (
""
)}
</ul>
</div>
<div></div>
</div>
);
}
}
Notes:
onclick should work on any element
You were trying to insert the form inside the onClick attribute. Instead, you should change the state of the component when li is clicked and based on the state show or hide the form
You don't seem to need a constructor in this example
import React from "react";
export default class Top extends React.Component{
state = {
show:false
}
showInput() {
return(
<div id='searchdiv'>
<form id='searchform'>
<input type="text" id="input" name="search"></input>
</form>
</div>
);
}
handleShow() {
this.setState({
show: !this.state.show
})
}
render(){
return(
<div>
<div id="navbar">
<ul id="nav">
<li><a className="a" href='https://www.google.com/'>Home</a></li>
<li><a className="b" href='https://www.google.com/'>Profile</a></li>
<li><a className="c" href='https://www.google.com/'>Pricing</a></li>
<li onClick={e => this.handleShow(e)} id='sch'>Search..</li>
{
this.state.show ? this.showInput() : null
}
</ul>
</div>
</div>
);
}
}
You have to maintain a state variable that can control when to display a component. In this case, show.

ReactJS - valid function not working when called in componentDidMount() and componentDidUpdate()

******** EDITED TO SIMPLIFY EXAMPLE AND CLARIFY REQUIREMENT AND PROBLEM **********
I'm stumped with this one, I hope someone can help.
I have a nav bar that I need to run a function on to add .active classes to li elements if they have descendants of a.active.
The menu system is a React component: -
import React, {Component} from "react";
import { Link, NavLink } from 'react-router-dom'
import {activateMenu} from './ActivateMenu'
class SidebarMenu extends React.Component {
componentDidMount() {
activateMenu()
}
componentDidUpdate() {
activateMenu()
}
render() {
const renderNavLink = (to, text, icon, renderArrow = false) => {
return(
<NavLink to={to}>
<i className="bullet">{icon}</i>
<span>{text}</span>
{renderArrow ? <span className="pull-right-container">
<i className="angle-left"><FaAngleLeft /></i>
</span> : null}
</NavLink>
)
}
return (
<ul className="sidebar-menu" data-widget="tree">
<li className="">
{renderNavLink('/','Home',<FaHome />)}
</li>
<li className="treeview">
{renderNavLink("#",'Users',<FaGroup />, true)}
<ul className="treeview-menu">
<li>
{renderNavLink(userSearchSlug,'Search',<FaSearch />)}
</li>
</ul>
</li>
<button onClick={activateMenu}>Press Me</button>
</ul>
)
}
}
export default SidebarMenu
This will give me an HTML structure like this: -
<ul class="sidebar-menu tree" data-widget="tree">
<li class="treeview">
<a href="#">
<i class="fa fa-dashboard"></i> <span>Links</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li>
<i class="fa fa-circle-o"></i> Link1
</li>
<li>
<i class="fa fa-circle-o"></i> Link2
</li>
</ul>
</li>
</ul>
After React has rendered the HTML, I need to trigger a click event on the the .treeview > a node if any a.active nodes are found under .treeview-menu. So: -
<li class="treeview">
<a href="#" *****TRIGGER CLICK EVENT*****>
<i class="fa fa-dashboard"></i> <span>Links</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li>
<i class="fa fa-circle-o *****.ACTIVE CLASS HERE****"></i> Link1
</li>
<li>
<i class="fa fa-circle-o"></i> Link2
</li>
</ul>
</li>
activeMenu() looks like this: -
$('ul.sidebar-menu li.treeview:not(.menu-open)').has('a.active').find('a').trigger( "click" );
This function works when called from onClick() from a button on the page but it is not working in componentDidMount() and componentDidUpdate(). The function will run (tested with console.log() but not affect the HTML as it should. However, if I run it from a Button, it works perfectly. It also works perfectly when HMR runs.
I've no idea why this is happening. Does anyone have any ideas?
This is probably happening because you're selecting the element directly rather than using refs, although it's hard to say because we have no idea what $('ul.sidebar-menu .treeview a').parent().has('a.active').parent().find('.treeview a') is selecting, which is why this kind of code is an antipattern.
React may be in some state where it's not prepared to handle click events at those points. Try using something like the following:
import React, {Component} from "react";
import { Link, NavLink } from 'react-router-dom'
class SidebarMenu extends React.Component {
constructor(props) {
super(props);
this.menuRefs = [];
}
componentDidUpdate() {
if (this.menuRefs.length) {
this.menuRefs[0].click();
}
}
render() {
const renderNavLink = (to, text, icon, renderArrow = false) => {
return(
<NavLink to={to} innerRef={ref => this.menuRefs.push(ref)}>
<i className="bullet">{icon}</i>
<span>{text}</span>
{renderArrow ? <span className="pull-right-container">
<i className="angle-left"><FaAngleLeft /></i>
</span> : null}
</NavLink>
)
}
return (
<ul className="sidebar-menu" data-widget="tree">
<li className="">
{renderNavLink('/','Home',<FaHome />)}
</li>
<li className="treeview">
{renderNavLink("#",'Users',<FaGroup />, true)}
<ul className="treeview-menu">
<li>
{renderNavLink(userSearchSlug,'Search',<FaSearch />)}
</li>
</ul>
</li>
<button onClick={() => this.menuRefs[0] && this.menuRefs[0].click()}>Press Me</button>
</ul>
)
}
}
export default SidebarMenu
Notice
Now there's an array of "menuRefs" and you just use them like normal DOM elements.
We push to the menuRefs in the NavLink innerRef prop (found here)
Note however that you may want to keep a map to ensure that no duplicates get pushed into menuRefs.
To learn more about refs, visit the docs: https://reactjs.org/docs/refs-and-the-dom.html

Categories