Reputation: 57
I tried to build a sticky navbar with a drop shadow when scrolled. I tried adding on scroll on the nav, but it doesn't work.
import React, { Component } from 'react'
import { MenuItems } from "./navbar"
import './navbar.css'
import { Button} from "../Button";
class Navbar extends Component {
state = { clicked : false}
scroll = {scrolled : false}
handleScroll = () => {
const offset = window.scrollY;
if (offset > 200) {
this.setState({scrolled : !this.scroll.scrolled})
}
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked})
}
render() {
return(
<div onScroll={this.handleScroll}>
<nav className={this.scroll.scrolled ? "NavbarItems" : "NavbarItems Scroll"}>
<h1 className="navbar-logo">React <i className="fab fa-react"></i></h1>
<div className="menu-icon" onClick={this.handleClick}>
<i className={this.state.clicked ? "fas fa-times" : 'fas fa-bars'}></i>
</div>
<ul className={this.state.clicked ? 'nav-menu active' : 'nav-menu'}>
{
MenuItems.map((items, index) => {
return (
<li key = {index}><a className={items.cName} href={items.url}>
{items.title}
</a></li>
);
})
}
</ul>
<Button> Contact US </Button>
</nav>
</div>
);
}
}
export default Navbar
What did I do wrong? is it not possible to do it like that?? I'm pretty new to react and javascript, so I'm guessing this isn't the right method to do it.
If there's any reference, please let me know. it would be delightful.
Upvotes: 2
Views: 3839
Reputation: 41
I tried the solution above , but it didn't work on scrolling so I tried to put the logic inside the useEffect, and it works perfectly. Here is the code with Functional component :
const Navbar = () => {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
window.onscroll = function() {
if (window.scrollY > 50) {
setScrolled(true);
} else {
setScrolled(false);
}
};
}, []);
return (
<div
className={scrolled ? "navbar-container floatingNav" : "navbar-container"}
>
Upvotes: 4
Reputation: 1490
Your state is not in constructor method. When we use extended classes in react js we initialize our state in constructor method. This is not needed when we use functional components, reducing complexity and one of many reasons why functional components gained popularity.
Besides it I've done more you can see comments in code explaining other things.
class Navbar extends Component {
constructor() {
super(); // super allows you to access parent class's methods and allows us to use "this." in constructor().
this.state = {
clicked: false,
scrolled: false,
};
// Note here too these bindings are necessary to make `this` work in the callback
// In general, we use binding whenever we use "setState" when handling an event
this.handleSroll = this.handleScroll.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleScroll = () => {
const offset = window.scrollY;
if (offset > 200) {
this.setState({ scrolled: !this.state.scrolled });
}
};
handleClick = () => {
this.setState({ clicked: !this.state.clicked });
};
render() {
return (
<div onScroll={this.handleScroll}>
{/* Note here this.scroll.scrolled changes to this.state.scrolled */}
<nav
className={this.state.scrolled ? "NavbarItems" : "NavbarItems Scroll"}
>
<h1 className="navbar-logo">
React <i className="fab fa-react"></i>
</h1>
<div className="menu-icon" onClick={this.handleClick}>
<i
className={this.state.clicked ? "fas fa-times" : "fas fa-bars"}
></i>
</div>
<ul className={this.state.clicked ? "nav-menu active" : "nav-menu"}>
{MenuItems.map((items, index) => {
return (
<li key={index}>
<a className={items.cName} href={items.url}>
{items.title}
</a>
</li>
);
})}
</ul>
<Button> Contact US </Button>
</nav>
</div>
);
}
}
export default Navbar;
More info on super();
Why we need to bind [info]
Go through react's official docs about handling events
I also suggest you to read about functional components.
Upvotes: 2
Reputation: 1531
your state should be in a constructor:
constructor(){
super();
this.state ={
clicked : false,
scrolled : false
}
}
full change:
import React, { Component } from 'react'
import { MenuItems } from "./navbar"
import './navbar.css'
import { Button} from "../Button";
class Navbar extends Component {
constructor(){
super();
this.state ={
clicked : false,
scrolled : false
}
}
handleScroll = () => {
const offset = window.scrollY;
if (offset > 200) {
this.setState({scrolled : !this.state.scrolled})
}
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked})
}
render() {
return(
<div onScroll={this.handleScroll}>
<nav className={this.state.scrolled ? "NavbarItems" : "NavbarItems Scroll"}>
<h1 className="navbar-logo">React <i className="fab fa-react"></i></h1>
<div className="menu-icon" onClick={this.handleClick}>
<i className={this.state.clicked ? "fas fa-times" : 'fas fa-bars'}></i>
</div>
<ul className={this.state.clicked ? 'nav-menu active' : 'nav-menu'}>
{
MenuItems.map((items, index) => {
return (
<li key = {index}><a className={items.cName} href={items.url}>
{items.title}
</a></li>
);
})
}
</ul>
<Button> Contact US </Button>
</nav>
</div>
);
}
}
export default Navbar
Does this fixes your problem?
Upvotes: 0