Why my useState doesn't work as it should? - reactjs

So I have a sidebar in my react app, and when the screen is mobile, it goes to the top and only the button "menu" remains, and when clicked it should display the sidebar.
Example is below:
Sidebar:
Sidebar with mobile screen(with clicked button):
So I have a code like this:
const Sidebar = () => {
const [sideBar, setSidebar] = useState(false);
return (
<div className="sidebar">
<span class="btn">Menu</span>
<div className="profile">
<span>Alim Budaev</span>
<span>Available for work</span>
</div>
<ul className="sidebarlist" id={setSidebar ? "hidden" : ""}>
{SlidebarData.map((val,key) =>{
return (
<li
className="row"
id={window.location.pathname == val.link ? "active" : ""}
key={key}
onClick={()=> {
window.location.pathname = val.link
}}>
{""}
<div>
{val.title}
</div>
</li>
);
})}
</ul>
</div>
);
}
export default Sidebar;
So as you could see, I'm using useState(), so when state is true, the "hidden" id should be added to ul and it should display, but state is false in my code, and it display anyway:
I'm not sure what could be the problem, but I think if state is false, the "hidden" id shouldn't be added, and the sidebar shouldn't be displayed.

Related

How can I use ref for another component?

So I have a sidebar with list of items, and when I click on an item, it should scroll to a certain div, which are outside components. Sidebar looks like this:
Sidebar component:
const Sidebar = () => {
const [sideBar, setSidebar] = useState(false);
return (
<div className="sidebar">
<span class="btn" onClick={() => setSidebar(!sideBar)}>Menu</span>
<div className="profile">
<img src={spike}/>
<span>Alim Budaev</span>
<span>Available for work</span>
</div>
<ul className="sidebarlist" id={sideBar ? "hidden" : ""}>
{SlidebarData.map((val,key) =>{
return (
<li
className="row"
id={window.location.pathname === val.link ? "active" : ""}
key={key}
onClick={()=> {
}}>
{""}
<div>
{val.title}
</div>
</li>
);
})}
</ul>
</div>
);
}
Components in App.js:
function App() {
return (
<div className="App">
<div className="header">
<Sidebar/>
<Hero">
<Particles/>
</Hero>
<About/>
<Service/>
<Form/>
<Footer/>
</div>
</div>
);
}
So I'm looking to way to scroll to a certain component when I click on . I know it can be made through useRef(), but I don't know how to do it in the Sidebar with outside components.

How can I set an element id to active when another component is scrolled into view?

So I have a sidebar and inside of it multiple buttons. When I click on the particular button, it scrolls into view a component with a certain name(I have one page with multiple components). And it works fine, components scroll into view,
but I want to set a list item id to active, according to the current component in view, so it changes color, but in the other li items active class is removed.
SideBar.jsx:
const Sidebar = () => {
const [sideBar, setSidebar] = useState(false);
return (
<div className="sidebar">
<span class="btn" onClick={() => setSidebar(!sideBar)}>
Menu
</span>
<div className="profile">
<img src={spike} />
<span>Alim Budaev</span>
<span>Available for work</span>
</div>
<ul className="sidebarlist" id={sideBar ? "hidden" : ""}>
{SlidebarData.map((val, key) => {
return (
<li
className="row"
id={val.link == val.title ? "active" : ""}
key={key}
onClick={() => {
document.getElementById(val.link).scrollIntoView();
}}
>
<div>{val.title}</div>
</li>
);
})}
</ul>
</div>
);
};
So as you can see, I have a ul with list items, and when I click on each one, it scrolls a certain div into view. I also SidebarData.js file, where I store all data as an array:
SidebarData.js
export const SlidebarData = [
{
title: "Home",
link: "home"
},
{
title: "About",
link: "about"
},
{
title: "Services",
link: "services"
},
{
title: "Contact",
link: "contact"
}
];
So when a particular div is in view, I want to set a li id to active, but I can't figure out how I can tell li to do it.
you're changing id instead of class on li and id can’t be duplicate, it is not assigned correctly.
Instead of using id in your code, you might use ref.
here is a sample code to add active class to li based on elements in view.
const Sidebar = () => {
const [sideBar, setSidebar] = useState(false);
const [selectedLink, setSelectedLink] = useState("");
return (
<div className="sidebar">
<span className="btn" onClick={() => setSidebar(!sideBar)}>
Menu
</span>
<div className="profile">
<img src={spike} />
<span>Alim Budaev</span>
<span>Available for work</span>
</div>
<ul className="sidebarlist" id={sideBar ? "hidden" : ""}>
{SlidebarData.map((val, key) => {
return (
<li
className={`row ${selectedLink === val.link ? "active" : ""}`}
id={val.link}
key={key}
onClick={() => {
setSelectedLink(val.link);
document.getElementById(val.link).scrollIntoView();
}}
>
<div>{val.title}</div>
</li>
);
})}
</ul>
</div>
);
};
There are two problems with your solution. One is that your SlidebarData has different titles and links. Thus, when using val.link === val.title in Sidebar hook, you're getting false on the condition, returning the id as blank.
On the other hand, I'm not sure how you're not getting an error with document.getElementById(val.link).scrollIntoView();. The value of val.link is going to be either Home, About, ...; however, you're setting the id of each li as either "active" or blank (""). So, document.getElementById(val.link) should return null and not the element.
EDIT: Does this solve your problem?
If you want to set the id which is active when pressed, do not use the keyword active as you'll have to change all the other id's. Create a state variable currentId (for example), and use it to set the current item you've selected.
const Sidebar = () => {
const [sideBar, setSidebar] = useState(false);
const [currentId, setCurrentId] = useState("");
return (
<div className="sidebar">
<span class="btn" onClick={() => setSidebar(!sideBar)}>
Menu
</span>
<div className="profile">
<span>Alim Budaev</span>
<span>Available for work</span>
</div>
<ul className="sidebarlist" id={sideBar ? "hidden" : ""}>
{SlidebarData.map((val, key) => {
return (
<li
className="row"
id={val.link}
key={key}
onClick={() => {
setCurrentId(val.link);
document.getElementById(val.link).scrollIntoView();
}}
>
<div>{val.title}</div>
</li>
);
})}
</ul>
<div>{currentId}</div>
</div>
);
}
Edit #2: Here is the codesandbox link, so you can have a look at the behaviour of the aforestated code.
https://codesandbox.io/s/fluent-ui-example-forked-rbd9p?file=/index.js
You'll need to use the Intersection Observer API, which will allow you to monitor and react to events which occur when tracked elements intersect a parent element (or the viewport).
Implementing this in React is non-trivial and will likely involve forwarding refs for each tracked element, however, you might find one or more community modules which already exist (e.g. react-intersection-observer).

How to open dynamic modal with react js

I am trying to convert the HTML/Javascript modal to React js.
In Reactjs, I just want to open the modal whenever the user clicks the View Project button.
I have created a parent component (Portfolio Screen) and a child component (Portfolio Modal). The data I have given to the child component is working fine but the modal opens the first time only and then does not open. Another problem is that the data does not load even when the modal is opened the first time.
Codesandbox link is here.
https://codesandbox.io/s/reverent-leftpad-lh7dl?file=/src/App.js&resolutionWidth=683&resolutionHeight=675
I have also shared the React code below.
For HTML/JavaScript code, here is the question I have asked before.
How to populate data in a modal Popup using react js. Maybe with hooks
Parent Component
import React, { useState } from 'react';
import '../assets/css/portfolio.scss';
import PortfolioModal from '../components/PortfolioModal';
import portfolioItems from '../data/portfolio';
const PortfolioScreen = () => {
const [portfolio, setportfolio] = useState({ data: null, show: false });
const Item = (portfolioItem) => {
setportfolio({
data: portfolioItem,
show: true,
});
};
return (
<>
<section className='portfolio-section sec-padding'>
<div className='container'>
<div className='row'>
<div className='section-title'>
<h2>Recent Work</h2>
</div>
</div>
<div className='row'>
{portfolioItems.map((portfolioItem) => (
<div className='portfolio-item' key={portfolioItem._id}>
<div className='portfolio-item-thumbnail'>
<img src={portfolioItem.image} alt='portfolio item thumb' />
<h3 className='portfolio-item-title'>
{portfolioItem.title}
</h3>
<button
onClick={() => Item(portfolioItem)}
type='button'
className='btn view-project-btn'>
View Project
</button>
</div>
</div>
))}
<PortfolioModal portfolioData={portfolio} show={portfolio.show} />
</div>
</div>
</section>
</>
);
};
export default PortfolioScreen;
Child Component
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const PortfolioModal = ({ portfolioData, show }) => {
const portfolioItem = portfolioData;
const [openModal, setopenModal] = useState({ showState: false });
useEffect(() => {
setopenModal({
showState: show,
});
}, [show]);
return (
<>
<div
className={`portfolio-popup ${
openModal.showState === true ? 'open' : ''
}`}>
<div className='pp-inner'>
<div className='pp-content'>
<div className='pp-header'>
<button
className='btn pp-close'
onClick={() =>
setopenModal({
showState: false,
})
}>
<i className='fas fa-times pp-close'></i>
</button>
<div className='pp-thumbnail'>
<img src={portfolioItem.image} alt={`${portfolioItem.title}`} />
</div>
<h3 className='portfolio-item-title'>{portfolioItem.title}</h3>
</div>
<div className='pp-body'>
<div className='portfolio-item-details'>
<div className='description'>
<p>{portfolioItem.description}</p>
</div>
<div className='general-info'>
<ul>
<li>
Created - <span>{portfolioItem.creatDate}</span>
</li>
<li>
Technology Used -
<span>{portfolioItem.technologyUsed}</span>
</li>
<li>
Role - <span>{portfolioItem.Role}</span>
</li>
<li>
View Live -
<span>
<NavLink to='#' target='_blank'>
{portfolioItem.domain}
</NavLink>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default PortfolioModal;
You don't have to use one useState hook to hold all your states. You can and I think you should break them up. In the PortfolioScreen component
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
I changed the function Item that is used to set the active portfolio item to toggleItem and changed it's implementation
const toggleItem = (portfolioItem) => {
setData(portfolioItem);
setVisible(portfolioItem !== null);
};
You should use conditional rendering on the PortfolioModal, so you won't need to pass a show prop to it, and you'll pass a closeModal prop to close the PortfolioModal when clicked
{visible === true && data !== null && (
<PortfolioModal
data={data}
closeModal={() => toggleItem()} // Pass nothing here so the default value will be null and the modal reset
/>
)}
Then in the PortfolioModal component, you expect two props, data and a closeModal function
const PortfolioModal = ({ data, closeModal }) => {
And the close button can be like
<button className="btn pp-close" onClick={closeModal}>
...

React dropdown function triggers all dropdowns instead of selected menu

So I'm trying to figure out how to only trigger the dropdown for the selected menu. Right now if I click on any of my menu items, it triggers every single dropdown.
I currently have a simple function that sets the state from false to true
const showSubnav = () => setSubnav(!subnav);
I attempted to use useRef() but for some reason the ref.current kept showing the wrong element that I clicked on.
Here is my current dropdown code
{SidebarData.map((item, index) => {
return (
<>
<li
ref={ref}
// Here's the function that checks if there's a sub menu, then it triggers
showSubnav
onClick={item.subNav && showSubnav}
key={index}
className={item.cName}
>
<Link to={item.path}>
{item.icon}
<span>{item.title}</span>
</Link>
</li>
{subnav ? (
<>
{item.subNav &&
item.subNav.map((item, index) => {
return (
<div key={index} className='sub-nav-container'>
<Link to={item.path} className={item.cName}>
{item.icon}
<span>{item.title}</span>
</Link>
</div>
);
})}
</>
) : null}
</>
);
})}
So the issue is that any li with a sub menu will display if I click on it using my code
onClick={item.subNav && showSubnav}
I need a function or way to check for the current element clicked and to only trigger that sub menu for that specific element.
I also have react icons that I used in my data file
So I'm trying to display them only if there's a sub nav
This code is the logic, but I can't seem to fit it anywhere properly
if(item.subNavExists) {
{item.downArrow}
} else if(subnav is click) {
{item.upArrow}
}
else {
return null
}
How would I fit this logic inside of my li tags?
<Link to={item.path}>
{item.icon}
<span>{item.title}</span>
</Link>
Try this approach,
Create a separate component for Sidebar and track each sidebar changes separately using local state like below,
import { Link } from "#material-ui/core";
import React, { useState } from "react";
import "./styles.css";
const SidebarData = [
{
id: 1,
title: "Item1"
},
{
id: 2,
title: "Item2"
}
];
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{SidebarData.map((item, index) => {
return <Sidebar item={item} />;
})}
</div>
);
}
const Sidebar = ({ item }) => {
const [selected, setSelected] = useState(false);
return (
<div class="container">
<button onClick={() => setSelected(!selected)}>
{selected ? "hide" : "show"}
</button>
<li key={item.id} className={item.cName}>
<Link to={item.path}>
{item.icon}
<span>{item.title}</span>
</Link>
</li>
{selected && <div>SUB-NAV</div>}
</div>
);
};
Sample demo - https://codesandbox.io/s/compassionate-booth-mj9xo?file=/src/App.js

How to close mobile menu in react when click on link?

I am making an app in react. I am facing issue as my mobile menu remains open even after clicking the navbar items. Does anyone know how it can be closed upon clicking on the navbar items?
class Navbar extends Component {
state = { clicked: false};
handleClick = () => {
this.setState({clicked:!this.state.clicked})
};
render () {
return (
<nav className={"NavbarItems"}>
<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((item, index) => {
return (
<div key={index}>
<li >
<Link className={item.cName} to={item.url}>
{item.title}
</Link>
</li>
</div>
)
})}
</ul>
<Button><Link className={"nav-button"} to="/contact">Contact Us</Link></Button>
</nav>
)
}
}
export default Navbar;
Could be like this:
<Link to={() => {
this.handleClick();
return '/contact'
}} >Contact Us</Link>

Resources