React onClick event not working as expected inside loop - reactjs

This is my code,
import "./styles.css";
import React from "react";
export default function App() {
const categories = [
{ name: "aaa", slug: "aaa" },
{ name: "baa", slug: "baa" }
];
const handleClick = (value) => {
console.log(value);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>
<ul>
{categories.map((category) => (
<li className="cat_item" key={category.slug}>
<a
value="value"
onClick={(e) => handleClick(e.currentTarget.value)}
className="dropdown-item"
>
{category.name}
<span className="text-gray-25 font-size-12 font-weight-normal"></span>
</a>
</li>
))}
</ul>
</div>
</div>
);
}
But whn I click it appear as undefined instead of value in log. Here is a fiddle of issue

This is because the target is the HTML anchor element itself and thus doesn't have a value property associated with it. In your case, I'm assuming you want to get the text within the your anchort element, try this instead.
onClick={(e) => handleClick(e.target.textContent)}
And we can use this for custom attributes,
onClick={(e) => handleClick(e.target.attributes.getNamedItem("value").value)}
Source is here

you have try to pass the value. But here no any input field that's why undefined here. You have try to pass innerText content.
onClick={(e) => handleClick(e.currentTarget.innerText)}

Related

Add dynamic link from an object in React

I'm new to React and I'm trying to insert a Link in a React Component. I made an object, and each item contains an external link. This the object :
export const myList =
[
{
"id":"P1",
"title":"Title1",
"description":"The first description",
"link":"https://random-link.io",
"cover":require("../img/myImg1.webp")
},
{
"id":"P2",
"title":"Title2",
"description":"The second description",
"link":"https://random-link2.io",
"cover":require("../img/my2ndImg.webp")
},
...
];
The main idea is to create pages for each item of the list, and a link to an external page to see more information.
I tried to do this :
function Page() {
const id = useParams();
const pageList = myList.find(list => list.id === id.id);
return(
<>
{
pageList ? (
<div className="Page">
<img className="ListCover" src={pageList?.cover} alt={pageList?.title}/>
<div className="information-list">
<span className="title-list">{pageList?.title}</span>
</div>
<div className="description-list">
<Dropdown titre="Description" description={pageList?.description} link={pageList?.link} />
</div>
</div>
) : <Navigate replace to="/404"/>
}
</>
)
}
export default Page;
In the Dropdown component, I made this :
function Dropdown({title, description, link}) {
const [open, setOpen] = useState(false);
return(
<div className="dropdown" id={`dropdown-${title}`}>
<div className="header-dropdown">
<div className="title-dropdown">{title}</div>
<span className={`arrow-dropdown ${open}`} onClick={() => setOpen(!open)}>
<img src={arrow} alt="Open it"/>
</span>
</div>
{
/* if dropdown is TRUE then it show the description */
open && <div className="description-dropdown">{description}
See more </div>
}
</div>
);
}
export default Dropdown;
The problem is that the link sends me to : http://localhost:3000/[object%20Object]; with another method I got http://localhost:3000/myProjet/https://random-link.io
I believe that the only issue in your code is that you are trying to use an object as the parameter for href which takes a string, try to just put link in there and it should work. it should look like:
<a href={link} rel='noreferrer'>

React sending props withoutid

<Description
description={description}
/>
In Description component I can display name and other pool but not id. I diplay in console.log what is in this props and it is all without id, created_at and updated_at. Why? How can I send props with id? Let me just mention that in parent component everything is ok.
#Update
export default function Description({description}){
const showDescription = (description) => {
console.log(description);
}
return(
<div>
<h2>{description.id} {description.title}</h2>
<div><button onClick={ () => showDescription(description) }
>Click</button></div>
</div>
);
}
#Update2
For the test I tryed send id like a new props. I show you too more code
<>
<ul>
{descriptions.map(description =>
{description.id}
<li
key={description.id}>
{description.id} <span onClick={
() => handleClick(desscription)}>{description.na
me}
</span>
</li>)}
</ul>
<Description
id={description.id}
description={description}
/>
</>
React display id in list (li) but if I send that in props, Description component desplay undefined.
import Description from "./Description";
export default function App() {
const descriptions = [
{ id: 1, title: "Foo", name: "Alex" },
{ id: 2, title: "Bar", name: "Mike" }
];
const handleClick = (d) => {
console.log(d);
};
return (
<div>
<ul>
{descriptions.map((description) => (
<li key={description.id}>
<span onClick={() => handleClick(description)}>
{description.name}
</span>
</li>
))}
</ul>
<Description id={descriptions.id} description={descriptions} />
</div>
);
}
Source: Difference between {} and () with .map with Reactjs
Example 1:
Example 2:
I have created the following demo:
https://codesandbox.io/s/divine-currying-xdt7w?file=/src/App.js

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}>
...

Hiding an element after a while in Next.js

I have a header component as a function component. I want show a popup when logo text is clicked. After for a time it should close automatically. I use hooks for state of popup. But set state function doesn't work in setTimeout function. How can fix this?
import Link from 'next/link'
import style from './header.module.css'
const Header = () => {
const [popupOpen, setPopupOpen] = React.useState(false)
return (
<header className={style.header}>
<nav className={style.nav}>
<div
className={style.popupContainer}
onClick={() => {
setPopupOpen(!popupOpen)
console.log(popupOpen)
setTimeout(() => {
console.log(popupOpen)
setPopupOpen(!popupOpen)
console.log(popupOpen)
}, 1000)
}}
>
<span className={style.logo}>Logo</span>
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
</div>
<ul className={style.ul}>
<li>
<Link href='/'>
<a>.home</a>
</Link>
</li>
<li>
<Link href='/contact'>
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
)
}
export default Header
Console log:
Let me suggests, this is the same question as:
React - useState - why setTimeout function does not have latest state value?
const _onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
setPopupOpen(popupOpen => !popupOpen)
}, 2000);
};
Its happening because setPopupOpen is asynchronous. So by the time setPopupOpen(!popupOpen) is called it has same value as onClick first setPopupOpen(!popupOpen) so eventually when it called both setPopup doing same state update i.e both updating as false. Better way is to usesetPopupOpen callback function to update the value. I added this code.
import { useState } from "react";
import Link from "next/link";
import style from "./style.module.css";
const Header = () => {
const [popupOpen, setPopupOpen] = useState(false);
const toggle = () => {
setPopupOpen((prev) => !prev);
};
const onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
toggle();
}, 1000);
};
return (
<header className={style.header}>
<nav className={style.nav}>
<div className={style.popupContainer} onClick={onClick}>
<span className={style.logo}>Logo</span>
{popupOpen && (
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
)}
</div>
<ul className={style.ul}>
<li>
<Link href="/">
<a>.home</a>
</Link>
</li>
<li>
<Link href="/contact">
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
);
};
export default function IndexPage() {
return (
<div>
<Header />
</div>
);
}
Here is the demo: https://codesandbox.io/s/pedantic-haibt-iqecz?file=/pages/index.js:0-1212

Resources