For the life of me I cannot understand why the click event is not firing in my functional component.
Here's my component:
import React, { useState, useEffect } from 'react';
import {
Collapse,
Navbar,
NavbarToggler,
Nav,
NavItem,
NavLink,
} from 'reactstrap';
import classNames from 'classnames';
import img from '../../img/logo/logo-white.svg';
const NavBar = () => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => {
console.log('toggllling', isOpen);
setIsOpen(!isOpen);
};
return (
<div
id="nav-wrap"
className={classNames({ 'bg-dark-blue': isOpen })}
>
<Navbar color="transparent" dark expand="lg" className="container">
<a className="navbar-brand" href="/">
<img alt="logo light" src={img} />
</a>
<NavbarToggler onClick={toggle} />
<Collapse isOpen={isOpen} navbar>
<Nav className="navbar-nav" navbar>
<NavItem className="active">
<NavLink href="/">First</NavLink>
</NavItem>
<NavItem>
<NavLink href="/">second</NavLink>
</NavItem>
<NavItem>
<NavLink href="/">Thord</NavLink>
</NavItem>
<NavItem className="separated">
<NavLink href="/">Fourth</NavLink>
</NavItem>
<NavItem>
<NavLink href="/">fifth</NavLink>
</NavItem>
<NavItem>
<NavLink href="/" className="btn btn-light">last</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>
</div>
);
};
export default NavBar;
It's a bit more advanced version of the navbar toggle example from the reactstrap page: https://reactstrap.github.io/components/navbar/
I can see from the React Devtools that the event is bound to the correct react component. If I replace toggle with something that should fire immediately like a function call, then it fires immediately. Yet when I click on the actual button, nothing happens. I even bound a click event to the document to see if there is something hidden above the button and logged out if the event propagates down to correct element - it did. so for the life of me, I cannot figure out why the toggle function never fires and why I never see 'toggllling' in my console. I've tried adding this onClick to any of the other elements in the component also - and it never works anywhere inside the component.
It's probably something really small, that I'm missing here, but for the life of me - I cannot figure out what it is exactly. Not sure what other bits of information could be useful here. Perhaps how I use the component:
ready(() => {
// Example:
const wrap = document.getElementById('nav-wrap');
if (wrap) {
// perform element replacement
const parent = wrap.parentNode;
const temp = document.createElement('div');
render(<NavBar element={wrap} />, temp);
parent.replaceChild(temp.querySelector('div#nav-wrap'), wrap);
}
});
Edit:
I simplified the component and results are the same - the event is not firing. When I debug it with browser devtools and add breakpoint at mouse click, then I can see that function call ends up in react-dom.development.js function noop() {} Why is that?
Have you tried a setState with a callback?
setIsOpen((prevState) => (
!prevState
));
Not sure how useful this answer is to the general population. There are not of onClick "not working" questions. Mods - if you see nothing to learn here, please just mark the question for deletion.
So what happened here was that I had this bit of a relic replacement mechanic in my index.js which created the component. I rendered the component inside element not in DOM:
const temp = document.createElement('div');
render(<NavBar element={wrap} />, temp);
And then looked it up and replaced its pure HTML predecessor with the react component. I went for this complex solution, because initially, I just tried to reuse some components. I later changed this logic, but did not go back to just using render(<NavBar/>, wrap);, which would have saved me all the trouble of writing this question.
So my own complexity, which I did not reduce on time, created even more overhead and wasted time. So yeah - avoid complexity. That's the lesson here.
Related
I am using Nextjs 13 with the experimental App Dir but am not sure if this problem I am facing has anything to do with the issue I am facing. I have an id in my home page of "faqs" and when I click on the link, I can see it successfully goes to that link but does nothing in the browser. If I am on another page, I click the link and it takes me to the home page with the correct url but still stays on the top of the page and does not scroll to the indicated id. I did implement scroll={false} as suggested in the documentation but it makes no difference.
Here is a snippet of the relevant code parts:
"use client"
import React, { useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
const Navigation = () => {
const router = useRouter()
...
In the return:
<Link scroll={false} href="/#faqs">FAQS</Link>
I Even tried:
<button type="button" onClick={() => router.push("/#faqs")}>FAQS</button>
In React the hash works fairly well but in next js, even only in client rendering it seems convoluted. If anyone knows what I am doing wrong or if there is a viable work around, I would sure appreciate it.
Thank you in advance.
If I am missing anything, please let me know.
I use hashtags a lot and I plan to start using the app directory in future projects, so I dug into this and it's not pretty. Apparently, NextJS uses a different package for app directory components client-side called "next/navigation". It's very different from "next/router". Also, when using "next/link" elements, NextJS does not trigger the onRouteChangeComplete event when location.hash changes but location.pathname does not.
So, in order to detect a hash change and scroll to the associated element, I finally had to implement this hack:
"use client"
import { Inter } from '#next/font/google'
import paragraph from './paragraph'
import Link from 'next/link'
import { useEffect, useState } from 'react'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
const [navClick, setNavClick] = useState(false);
useEffect(() => {
setTimeout(() => {
const hash = window.location.hash;
if (hash) document.querySelector(hash).scrollIntoView();
}, 0);
}, [navClick])
const toggleNavClick = () => setNavClick((oldVal) => !oldVal);
return (
<main>
<nav>
<ul>
<li>
<Link href="/#one" onClick={toggleNavClick}>Section One</Link>
</li>
<li>
<Link href="/#two" onClick={toggleNavClick}>Section Two</Link>
</li>
<li>
<Link href="/#three" onClick={toggleNavClick}>Section Three</Link>
</li>
</ul>
</nav>
<div className="container">
<section id="one">
<h1>Section One</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="two">
<h1>Section Two</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="three">
<h1>Section Three</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
</div>
</main>
)
}
Since the hash change cannot be detected because no event is triggered, I basically created an event by toggling navClick each time a link is clicked. The navigation logic is enclosed in setTimeout() function because it triggers after window.location is updated.
Repo: https://github.com/designly1/next-hash-test
Demo: https://next-hash-test.vercel.app/
I am trying to make a dashboard for teachers where the details of the teachers would be displayed. I am using nested routing to display the components related to the teacher's dashboard and therefore I have sidebar too. I was facing a flicker in the layout of the side bar while navigating from one route to another about which i have mentioned in my another question How stop the flicker in the SideNav caused by the useEffect which fetches some data on every render - Reactjs. So I was playing with code and did some console logs to track exactly what is happening to the component and found out that there is a unusual rendering of the components. I am making an api call inside the useEffect of the parent and do not know if thats causing an issue. Below is all the codes of the parent and child
Dashboard (Parent Component)
const TeacherDashboard = () => {
// selecting slices for getting user data
const { authTokens, user } = useSelector((state) => state.login);
// setting states
const [teacher, setTeacher] = useState(null); //to store the teacher detail
const [loading, setLoading] = useState(true);
useEffect(() => {
// setting the loading tru to wait for the data
setLoading(true);
/*
this is the procedure to retrive data from an async api call because we
cannot directly use async calls so we have to wrap them inside another small function.
*/
const fectData = async () => {
//await here is neccessary to wait for the promise to get resolved
let response = await fetchTeacherDetail(user.user_id);
setTeacher(response.data);
setLoading(false);
};
fectData();
}, []);
console.log("Rendered Dashboard");
return (
<Container>
<SideBar name={!loading && teacher.name} />
<ContentMainContainer>
{/* this makes the nested routes display here */}
<Outlet />
</ContentMainContainer>
</Container>
);
};
SideBar (Child Component)
const SideBar = (props) => {
// selecting slices for getting user data
const { authTokens, user } = useSelector((state) => state.login);
// setting states
const [teacher, setTeacher] = useState(null); //to store the teacher detail
const [loading, setLoading] = useState(true);
console.log("The SideBar has rendered");
const profileButton = (
<Fragment>
<NavLink activeclassname="acitve" to="/teacher/profile">
<i className="fas fa-user"></i>Profile
</NavLink>
</Fragment>
);
const enterDetails = (
<Fragment>
<NavLink activeclassname="acitve" to="/teacher/profile">
<i className="fas fa-pen"></i> Enter Details
</NavLink>
</Fragment>
);
return (
<SideNav>
<UserNameContainer>
<p>{props.name}</p>
</UserNameContainer>
<ButtonContainer>
{/* <Fragment>
{!loading && teacher.is_verified ? profileButton : enterDetails}
</Fragment> */}
<NavLink activeclassname="acitve" to="info">
<i className="fas fa-info-circle"></i> My Information
</NavLink>
<NavLink activeclassname="acitve" to="student-requests">
<i className="fas fa-user-plus"></i> Requests
</NavLink>
<NavLink activeclassname="acitve" to="enrolled-student">
<i className="fas fa-user-graduate"></i> My Students
</NavLink>
<NavLink activeclassname="acitve" to="payment">
<i className="fas fa-rupee-sign"></i> Payments
</NavLink>
</ButtonContainer>
</SideNav>
);
};
export default memo(SideBar);
And now the Console Logs about how the components are rendering
on first load after login in
Rendered Dashboard
SideBar.js:16 The SideBar has rendered
TeacherDashboard.js:35 use effect called
TeacherDashboard.js:38 Rendered Dashboard
TeacherDashboard.js:38 Rendered Dashboard
SideBar.js:16 The SideBar has rendered
next on clicking on any link to route to one of the nested route
Rendered Dashboard
SideBar.js:16 The SideBar has rendered
TeacherDashboard.js:35 use effect called
TeacherDashboard.js:38 Rendered Dashboard
TeacherDashboard.js:38 Rendered Dashboard
SideBar.js:16 The SideBar has rendered
on the third click
Rendered Dashboard
SideBar.js:16 The SideBar has rendered
TeacherDashboard.js:38 Rendered Dashboard
SideBar.js:16 The SideBar has rendered
TeacherDashboard.js:35 use effect called
TeacherDashboard.js:38 Rendered Dashboard
TeacherDashboard.js:38 Rendered Dashboard
SideBar.js:16 The SideBar has rendered
the components are getting rendered in this pattern. Please help me to rectify this problem of unusual re-rendering and eventually stop the flicker. I am new to react do not understand how to solve it
The Flickering Problem on the side bar
Every time you trigger the useEffect in the parent, you set the loading state to true. This will cause the name prop you send to the SideBar to toggle between a string and false/undefined, which will trigger a re-render in the Sidebar
i need to click two times on the button to update the state.
i have two buttons. when i toggles between the buttons the text should change.
I found some questions related to this but did not worked for me.
please find Below code.
import React,{useState} from "react";
import { BrowserRouter as Router, NavLink } from "react-router-dom";
import './SectionConnect.css';
const SectionConnect = () => {
const [activeButton, setActiveButton] = useState("company");
function toggleButton() {
setActiveButton(activeButton==="company" ? "individual" : "company");
}
return (
<div className="connect-container">
<div className="connect-wrapper">
<h2>How it works</h2>
{activeButton==="company"?<p>
We help your employees maximise potential through a bespoke, science-backed wellbeing platform.
</p>:<p>
We connect you with the very best practitioners and most effective
methods to achieve optimal health.
</p>}
<div className="connect-btn">
<NavLink
to="/"
exact
activeClassName="active-btn"
className="disable-btn"
onClick={toggleButton}
>
For companies
</NavLink>
<NavLink
to="/forIndividuals"
exact
activeClassName="active-btn"
className="disable-btn"
onClick={toggleButton}
>
For individuals
</NavLink>
</div>
</div>
</div>
);
};
export default SectionConnect
Not able to understand where i am going wrong.
Thanks in advance!
on toggleButton call like this onClick={()=> toggleButton()}
I'm setting up a navigation for a react app that I'm currently working on.
I have a full screen navigation which overlays the website when a button in the header is clicked.
I've used useState to toggle the overlay on and off which is working great but I need to find a way to automatically hide the navigation overlay when a page link within the nav is clicked.
This is my Nav component:
import React, { useState } from 'react';
import { NavLink as Link } from 'react-router-dom';
function Navigation(){
const[showMenu, setShowMenu] = useState(false)
let menu
if(showMenu){
menu =
<nav>
<ul>
<Link to="/" exact activeStyle={{color: 'black'}}><li>Home</li></Link>
<Link to="/about"><li>About</li></Link>
<Link to="/work"><li>Work</li></Link>
<Link to="/hire"><li>Hire Me</li></Link>
</ul>
</nav>
}
return(
<>
<button className="nav-btn" onClick={() => setShowMenu(!showMenu)}>Menu</button>
{menu}
</>
);
}
export default Navigation;
I've tried adding onClick to the links like this:
<Link onClick={() => useState(false)} exact activeStyle={{color: 'black'}} to="/">
<li>Home</li>
</Link>
but I just get a compiler error "React Hook "useState" cannot be called inside a callback." So That's me out of ideas.
Any guidance would be greatly appreciated, as I'm learning React as I go.
useState has to be executed from a method. Simply create a method and set useState inside it.
function navigate(){
useState(false)
}
. . . . . . .
<Link onClick={navigate} exact activeStyle={{color: 'black'}} to="/">
<li>Home</li>
</Link>
Managed to figure it out.
I was right with the OnClick listener, but not quite there with the rest.
<Link onClick={() => setShowMenu(false)} to="/" exact activeStyle={{color: 'black'}}>
<li>Home</li>
</Link>
How can I remove the active Nav Item background feature in react-bootstrap when selecting Nav Item? It acting abnormally when I use it with 'react-redux' and 'react-router-bootstrap'?
For instance, when I reload the home page, the active background only stays on one of the Nav Items even if I select other Nav Items. Below is the image of the navitem (Dashboard) item is selected and the code for the navbar! Any recommendations would be greatly appreciate!
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {LinkContainer, IndexLinkContainer} from 'react-router-bootstrap';
import {
Nav,
NavItem,
Navbar
} from 'react-bootstrap';
class NavBar extends Component {
render() {
const {authenticated} = this.props;
return (
<Navbar>
<Navbar.Header>
<Navbar.Brand>
Timesheet App
</Navbar.Brand>
<Navbar.Toggle/>
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<IndexLinkContainer to="/">
<NavItem className="nav-link" eventKey={1}>Dashboard</NavItem>
</IndexLinkContainer>
<LinkContainer to="/timesheet/new">
<NavItem className="nav-link" eventKey={2}>Submit Time</NavItem>
</LinkContainer>
<LinkContainer to="/user/Andriy">
<NavItem className="nav-link" eventKey={3}>Andriy Time</NavItem>
</LinkContainer>
{authenticated &&
<LinkContainer to="/signout">
<NavItem className="nav-link" eventKey={4}>Sign Out</NavItem>
</LinkContainer>}
{!authenticated &&
<LinkContainer to="/signin">
<NavItem className="nav-link" eventKey={5}>Sign In</NavItem>
</LinkContainer>}
{!authenticated &&
<LinkContainer to="/signup">
<NavItem className="nav-link">Sign Up</NavItem>
</LinkContainer>}
</Nav>
</Navbar.Collapse>
</Navbar>
);
}
}
NavBar.propTypes = {
authenticated: PropTypes.bool
};
function mapStateToProps(state) {
return {
authenticated: state.auth.authenticated
};
}
export default connect(mapStateToProps)(NavBar);
If you want the active background gone, this can just be a CSS-only fix.
Just override the CSS for the active state background/border/etc to be the same as the non-active state.
Hope this helps.
You should create a class as a variable and use classNames to changed it based on whatever logic you decide to use.
You can then define something in CSS to deal with remove the active background.
For example (taken from the docs):
var btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
You can use different states to dictate what classes should be added and removed. This also helps with DRY principles as it will prevent you having to repeat yourself across all of the Navbar.
If you set activeKey="", then none of the links are active, and it's only the active key which gets a background.
<Nav activeKey="">
...
</Nav>