Add onScroll Event to React NavBar - javascript

I'm trying to add an onScroll event to my React navbar. What's supposed to happen is whenever the user scrolls and the collapsible navbar is open, it will make the navbar collapsed.
I'm not sure if I'm going about it the right way, as I'm getting compilation errors, which I'm not sure how to fix.
Relevant Code
import React, { useState } from 'react';
import Slide from 'react-reveal/Slide';
import onClickOutside from 'react-onclickoutside';
// CSS import statements
import '../css/NavBar.css'
function NavBar() {
const [open, setOpen] = useState(false);
NavBar.handleClickOutside = () => setOpen(false);
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll(event) {
setOpen(false);
}
return(
<Slide top>
<nav onScroll={handleScroll}>
<div className='navbar-container'>
<h4 className='logo'>Daniel Zhang</h4>
<div className='toggle-button' onClick={() => setOpen(!open)}>
<div className='bar' />
<div className='bar' />
<div className='bar' />
</div>
</div>
<div id='nav-links' className={open ? 'nav-open' : 'nav-collasped'}>
<a href='/'>Home</a>
<a href='/about'>About</a>
<a href='/blog'>Blog</a>
<a href='/contact'>Contact</a>
</div>
</nav>
</Slide>
);
}
const clickOutsideConfig = {
handleClickOutside: () => NavBar.handleClickOutside,
};
export default onClickOutside(NavBar, clickOutsideConfig);

Related

Showing Item details in Reactjs component using Laravel API

I have a list of fruits I want show their details. I have built the cards to display the list, and now I want to build a page that shows details for every item and open up its corresponding card list is clicked. I keep getting an error
TypeError: Cannot read properties of undefined (reading 'name')
The list cards work fine, and the individual item api also works fine. But now I cant display the details. Kindly help.
The Card Component
import React from 'react';
import { Link } from "react-router-dom";
import { LazyLoadImage } from "react-lazy-load-image-component";
const FruitWidget = ({ fruit }) => {
return (
<div className="col-6">
<div className="card mb-2">
<Link to={"/fruit-details/"+fruit.id}>
<LazyLoadImage src="assets/img/sample/photo/product1.jpg"
className="card-img-top lazycolor" alt="image" />
<div className="card-body">
<h4 className="mb-0">{fruit.name}</h4>
</div>
</Link>
</div>
</div>
);
}
export default FruitWidget;
The Items List
import React, { useState, useEffect } from 'react';
import { useNavigate } from "react-router-dom";
import Menubar from "../../components/menubar/Menubar"
import FruitWidget from "../../components/widgets/FruitWidget"
import { LazyLoadImage } from "react-lazy-load-image-component";
import WidgetSkeleton from "../../components/skeleton/WidgetSkeleton"
function Training() {
let navigate = useNavigate();
const [fruits, setFruits] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch("http://localhost:8000/api/fruits")
.then((result) => result.json())
.then((fruits) => {
setFruits(fruits);
setIsLoading(false);
});
}, []);
console.warn("result", fruits)
return (
<div className="section">
<div className="row">
{isLoading && <WidgetSkeleton cards={6} />}
{fruits.map((fruit) => (
<FruitWidget fruit={fruit} key={fruit.id} />
))}
</div>
</div>
);
}
export default Training;
The Details Component
import React, { useState, useEffect } from 'react';
import { useNavigate, useParams } from "react-router-dom";
import Menubar from "../../components/menubar/Menubar"
import Tabs from "../../components/fruittabs/FruitTabs";
function FruitDetails() {
let navigate = useNavigate();
const [fruit, setFruit] = useState({});
const { fruitId } = useParams();
useEffect(() => {
fetch(`http://localhost:8000/api/fruit/${fruitId}`)
.then((result) => result.json())
.then((fruit) => {
setFruit(fruit[0]);
});
}, [fruitId]);
return (
<div>
<Menubar />
<div className="appHeader bg-primary text-light">
<div className="left">
<a onClick={() => navigate(-1)} className="headerButton goBack">
<i className="fi fi-rr-angle-left"></i> </a>
</div>
<div className="pageTitle">{fruit.name}</div>
<div className="right"></div>
</div>
<div id="appCapsule">
<div className="section mt-3 mb-3">
<img src="assets/img/lazyload.svg"
alt="image" className="imaged img-fluid fruit-detail-main" />
</div>
<div className="section mt-3 mb-3">
<div>
<Tabs>
<div label="Details">
{fruit.details}
</div>
<div label="Infestations">
After 'while, <em>Crocodile</em>!
</div>
<div label="Advice">
Nothing to see here, this tab is <em>extinct</em>!
</div>
</Tabs>
</div>
</div>
</div>
</div>
);
}
export default FruitDetails;

I created a Modal using createPortal() method to render it. Then I found that modal renders twice

When the button inside the Post clicked, Popup will render with createPortal method outside from root element's tree.
With this code that popup renders twice.
I want to render it only once.
Here's the parent Post component.
import { useState } from 'react';
import PopupModal from './PopupModal/PopupModal';
import './Post.css';
const Post = (props) => {
const postData = props;
const [isOpen, setIsOpen] = useState(false);
return (
<div className="post-container">
<div className="post-img-container">
<img className="post-img" src={props.img} alt="Travels" />
</div>
<div className="post-text-container">
<h4 className="post-heading">{props.title}</h4>
<p className="post-para">{props.description}</p>
<h1 className="post-price">{props.price}</h1>
<div className="post-btn-container">
<button onClick={() => setIsOpen(true)} className="post-btn">
Check Availability
</button>
<PopupModal dataData={postData} open={isOpen} onClose={() => setIsOpen(false)}>
Button123
</PopupModal>
</div>
</div>
</div>
);
};
export default Post;
And here's the popupModal
import React from 'react';
import ReactDOM from 'react-dom';
import '../PopupModal/popupModal.css'
const MODAL_STYLES = {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%,-50%)',
background: '#fff',
width: '40vw',
height: '90vh',
padding: '50px',
zIndex: 1000,
};
const PopupModal = ({ open, children, onClose ,dataData }) => {
if (!open) return null;
console.log('xxx');
console.log(dataData);
return ReactDOM.createPortal(
<>
<div className='modal-overlay' ></div>
<div className='modal-container'>
<button onClick={onClose}> Popup Close</button>
{children}
</div>
</>,
document.getElementById('portal')
);
};
export default PopupModal;
Here's how I figured it rendered twice.
Here's the Popup with overlay around it which covers the background.
Thanks in advance!
Try following
{
isOpen && <PopupModal dataData={postData} open={isOpen} onClose={() => setIsOpen(false)}>
Button123
</PopupModal>
}

How do I only show render when the render has completed in React?

Hope this doesn't sound like a stupid question, but all queries I've searched on here and Google ask about only showing the render once fetches/requests have completed.
I want my React app to only show the render once the render has completed, including the CSS. At the moment, in a fraction of a second, you can see the page being built - in under a split second, but still it's not a fluid flow for the UX. Is there a way to only load the page once the render (including the CSS) is all done? I don't want to do a setTimeout with a loading page as that is very clunky.
Many thanks in advance
Code below:
import React, { useEffect, useContext } from 'react';
import { NavLink } from 'react-router-dom';
import axios from 'axios';
import '../../styles/MleaveReqUpper.css';
// import '../../styles/leaveRequests.css';
import leftArrow from '../../img/general/leftArrow.svg';
import teamsGrad from '../../img/general/teamsGrad.png';
import returnBack from '../../img/general/returnBack.svg';
import cog from '../../img/general/cog.svg';
import checklist from '../../img/general/checklist.svg';
import { DataContext } from '../../contexts/DataContext';
import $ from 'jquery';
import requestsSelected from '../../img/mFooter/requestsSelected.svg';
const MLeaveReqUpperLinks = () => {
const { teamAllows, toggleTeamAllows } = useContext(DataContext);
const navBlue = () => {
$('.f3').attr('src', requestsSelected);
};
useEffect(() => {
axios.get('db.json').then();
});
// render
return (
<div className='leaveReqUpperContainer'>
<img className='teamGradientOut' src={teamsGrad} />
<NavLink to='/requests'>
<div
className='backGroup'
onClick={() => {
navBlue();
if (teamAllows) {
toggleTeamAllows(false);
}
}}
>
<img
className='returnBack'
src={returnBack}
alt='Back to My Requests'
/>
</div>
</NavLink>
<h3 className='TeamRequests'>Team Requests</h3>
<div className='iconsM'>
<NavLink to='team-allowances'>
<img
onClick={() => {
toggleTeamAllows();
}}
className={`checklist ${!!teamAllows ? 'iconSelected' : ''}`}
src={checklist}
alt='Allowances'
/>
</NavLink>
<img className='cog' src={cog} alt='Settings' />
</div>
<div className='teamsXbar'>
<div className='teamsInnerContainer'>
<div className='teamMenuHolder'>
<p className='teamName teamSel '>All Staff</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Brewery</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Sales</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Finance</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Operations</p>
</div>
<div className='teamMenuHolder'>
<p className='teamName'>Marketing</p>
</div>
</div>
</div>
</div>
);
};
export default MLeaveReqUpperLinks;
You can use a loading state variable
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
axios.get('db.json').then(res=>setIsLoading(false););
});
return isLoading ? null : <div>All your view</div>

React way to open a NavBar onClick on a button

I trying to find a way to open the navbar of ReactJS app when i'm clicking on my "MENU" button.
At the beginning my nav component have a width of 0px (with overflow : hidden). When i'm clicking on the button my nav should have a width of 400px. I'm a beginner with React.
I have two React Components :
Topbar
export default function Topbar() {
return (
<div className="topbar__container">
<div className='topbar__menuButton'>
<Link className="topbar__link">MENU</Link>
</div>
<div className="topbar__title">
<Link to="/" className="topbar__link">EDGAR</Link>
</div>
</div>
)
}
Nav
const Nav = () => {
return (
<div className="navbar__container">
<Query query={CATEGORIES_QUERY} id={null}>
{({ data: { categories } }) => {
return (
<nav className="nav">
<ul>
{categories.map((category, i) => {
return (
<li key={category.id}>
<Link to={`/category/${category.id}`} className="nav__link">
{category.name}
</Link>
</li>
)
})}
</ul>
</nav>
)
}}
</Query>
</div>
)
}
export default Nav
To achieve something like that you have to set this logic in the common parent of both component (here App for the example).
App will manage a state to determine if the Nav is open or not. The state is called isMenuOpen and can be changed using the setIsMenuOpen() function. We will give to the children Nav the state isMenuOpen and to the children TopBar a callback from the function setIsMenuOpen():
App.jsx
import React from "react";
import TopBar from "./TopBar";
import Nav from "./Nav";
export default function App() {
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
return (
<div className="App">
<TopBar setMenuStatus={setIsMenuOpen} />
<Nav isOpen={isMenuOpen} />
</div>
);
}
Then the TopBar have to set the value of isMenuOpen to true using the function setIsMenuOpen() from the props.
TopBar.jsx
import React from "react";
export default function Topbar({ setMenuStatus }) {
return (
<div className="topbar__container">
<div className="topbar__menuButton">
<button
type="button"
onClick={() => {
setMenuStatus(true);
}}
>
Menu
</button>
</div>
</div>
);
}
Then the component Nav will set a specific class (here .open) if isOpen coming from props is true.
Nav.jsx
import React from "react";
import "./styles.css";
export default function Nav({ isOpen }) {
return (
<div id="nav" className={isOpen ? "open" : ""}>
Menu
</div>
);
}
styles.css
#nav {
display: none;
}
#nav.open {
height: 400px;
display: inline-block;
}
You can try this example in this codesandbox.
import React, {useState} from "react";
import "./styles.css";
export default function App() {
const [toggle, setToggle]= React.useState(false)
const [width, setWidth]= React.useState('')
const showMenu = () => {
setToggle(!toggle)
if(toggle === true) {
setWidth('50px')
}else {
setWidth('500px')
}
}
return (
<div className="App">
<button onClick={showMenu}>Menu</button>
<div style={{width, border:'1px solid red'}}>
<li>text</li>
<li>text</li>
<li>text</li>
<li>text</li>
</div>
</div>
);
}
reproducing link: https://codesandbox.io/s/billowing-flower-rxdk3?file=/src/App.js:0-592

onClick not invoked when clicking on sidebar

In my main container the layout has this code
import React, { Component } from 'react';
import Header from '../../components/Navigation/Header/Header';
import SideBar from '../../components/Navigation/SideBar/SideBar';
class Layout extends Component {
state = {
showSideBar: true
}
sideBarToggleHandler = () => {
console.log("test");
}
render() {
return (
<div>
<Header />
<div>
<SideBar onClick={this.sideBarToggleHandler}/>
<main id="main">
{this.props.children}
</main>
</div>
</div>
)
}
}
export default Layout;
Whenever I click on any element in the side bar I want to console log test
For some reason this is not happening however if I move the onClick method to the header for example or to main it works fine
This is my sidebar:
import React from 'react';
import classes from './SideBar.module.scss';
import Logo from '../Logo/Logo'
import NavigationItems from './NavigationItems/NavigationItems'
const sideBar = (props) => {
}
return (
<div className={Classes.SideBar}>
<Logo />
<nav>
<NavigationItems />
</nav>
</div>
);
};
export default sideBar;
and this is my navigation items:
import React from 'react';
import classes from './NavigationItems.module.scss';
import Aux from '../../../../hoc/Aux';
import CollapseIcon from '../../../../assets/Images/Icons/collapse.svg'
import { library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faHome } from '#fortawesome/free-solid-svg-icons';
import { faFileAlt } from '#fortawesome/free-solid-svg-icons';
import { faChartLine } from '#fortawesome/free-solid-svg-icons';
library.add(faHome);
library.add(faFileAlt);
library.add(faChartLine);
const navigationItems = (props) => {
return (
<Aux>
<p>CONSUMER</p>
<ul className={classes.NavigationItems}>
<li><FontAwesomeIcon className={classes.Icon1Paddig} icon="home" /> Home</li>
<li><FontAwesomeIcon className={classes.Icon2Paddig} icon="file-alt" /> Dashboard</li>
<li><FontAwesomeIcon className={classes.Icon3Paddig} icon="chart-line" /> Statistics</li>
</ul>
<div className={classes.Divider}></div>
<div className={classes.ButtonPosition}>
<button onClick={props.clicked}><img className={classes.CollapseIcon} src={CollapseIcon} alt='icon'></img>Collapse sidebar</button>
</div>
<div className={classes.Divider + ' ' + classes.DividerBottom}></div>
<p className={classes.footer}>Micheal Alfa v 1.0.0</p>
<p className={classes.footer}>Copyrights # 2019 All Rights Reserved</p>
</Aux>
);
};
export default navigationItems;
Any ideas?
Thank you
You are not doing anything with the passed onClick event. You need to put it on something, like so:
const sideBar = (props) => (
<div onClick={this.props.onClick} className={Classes.SideBar}>
<Logo />
<nav>
<NavigationItems />
</nav>
</div>
);
export default sideBar;
This will fire that event when you click on the div. Make sense?

Categories

Resources