React: The onClick nested within a card is not called - reactjs

I am using react hooks, and I have erased out other functions not associated with the function I am intending to call. The App functions are rendered
function App() {
const memoizedCallback = React.useCallback(() => {
console.log("Click happened");
}, []);
return (
<div className="App">
<ReactMapGl
{...viewport}
mapboxApiAccessToken={accesstoken}
onViewportChange={viewport => {
setviewport(viewport);
}}
>
{details.map(details => (
<Marker
key={details.name}
latitude={details.lat}
longitude={details.long}
>
<button
class="marker-btn"
onClick={e => {
e.preventDefault();
useselectedpark(details);
}}
>
<img src={icon} alt="icon" className="navbar-brand" />
</button>
</Marker>
))}
{selectedpark ? (
<Popup
latitude={selectedpark.lat}
longitude={selectedpark.long}
onClose={() => {
useselectedpark(null);
}}
>
<div>
<Card style={{ width: "18rem" }}>
<Card.Body>
<Card.Title>{selectedpark.name}</Card.Title>
<Card.Text>{selectedpark.postalcode}</Card.Text>
<div>
<Button variant="primary" onClick={memoizedCallback}>
Visit Gallery
</Button>
</div>
</Card.Body>
</Card>
</div>
</Popup>
) : null}
{console.log("in render", details)}
</ReactMapGl>
</div>
);
}
export default App;
the function I am intending to call is memoizedCallback. The function is called in an onClick of a button within a card.
The sequence of events is as such. A popup appears, and the user has an option to click on a button within the card that appears.
Problem: Currently, when the button is clicked right now, the function memoizedCallback is not called.
Why is that so, what did I miss here?

Related

React can't perform state update on unmounted component - checking isMounted not fixing

I am using axios to return data from an API and trying to present this in to various nested components in my React App.
The code looks something like this:
const Building = () => {
const { bid } = useParams();
const { userAccessToken } = useAuth();
const [buildingData, setBuildingData] = useState([]);
const bearerToken = `Bearer ${userAccessToken}`;
React.useEffect(() => {
let isMounted = true;
const axiosConfig = {
headers: { Authorization: bearerToken },
};
axios
.get(
"http://localhost:3001/building?requestedlid=2&requestedbid=" + bid,
axiosConfig
)
.then(function (response) {
if (isMounted) {
setBuildingData(response.data[0]);
}
})
.catch(function (error) {
// handle error
console.log(error);
});
return () => {
isMounted = false;
};
}, [bearerToken, bid]);
return (
<React.Fragment>
<Helmet title="Building Profile" />
<Container fluid className="p-0">
<Breadcrumb className="float-end mt-2">
<Breadcrumb.Item href="/dashboard/default">Home</Breadcrumb.Item>
<Breadcrumb.Item href="/buildings/portfolio">
Portfolio
</Breadcrumb.Item>
<Breadcrumb.Item active>Building Profile</Breadcrumb.Item>
</Breadcrumb>
<h1 className="h3 mb-3">
Building Profile
<OffcanvasHelp
id="buildingprofile"
name="Building Profile"
scroll
backdrop
/>
</h1>
<div className="clearfix"></div>
<Row>
<Col xl="8">
<BuildingProfile
name={buildingData.building_name}
status={buildingData.status}
description={buildingData.description}
keycontacts={buildingData.key_contacts}
created={buildingData.stats.created_date}
golive={buildingData.stats.golive_date}
/>
<Rooms />
</Col>
<Col xl="4">
<AccountManager />
<Map location={buildingData.location} />
<GetSupport type="commercial" />
</Col>
</Row>
</Container>
</React.Fragment>
);
};
My problem is I am receiving the common error:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Using the methods described in many StackOverflow answers, to check an isMounted boolean as in my code above, I am unable to resolve the issue.
Weirdly, the issue specifically occurs when I am passing these two props to the BuildingProfile component:
created={buildingData.stats.created_date}
golive={buildingData.stats.golive_date}
If I don't pass these two props, everything else works fine.
I'm sure I'm missing something silly but after several hours of trying to figure it out I'm still stuck. Anybody who can provide a pointer or any tips, I would be really grateful.
Many thanks
--- Update - including the BuildingProfile component:
const BuildingProfile = ({
name,
status,
description,
created,
golive,
keycontacts,
}) => {
// Modal config for "Deactivate Building"
const initOpenModals = () => {
let modals = {};
colors.forEach((color, index) => {
modals = Object.assign({}, modals, { [index]: false });
});
console.log(modals);
return modals;
};
const [openModals, setOpenModals] = useState(() => initOpenModals());
const toggle = (index) => {
// Toggle selected element
setOpenModals((openModals) =>
Object.assign({}, openModals, { [index]: !openModals[index] })
);
};
const notyf = useContext(NotyfContext);
const [type] = useState("success");
const [duration] = useState("5000");
const [ripple] = useState(true);
const [dismissible] = useState(false);
const [positionX] = useState("right");
const [positionY] = useState("top");
const navigate = useNavigate();
return (
<Card>
<Card.Header className="mb-0 pb-0">
<Card.Title className="mb-0">
<IsAllowed to="edit:buildings">
<div className="card-actions float-end">
<Dropdown align="end">
<Dropdown.Toggle as="a" bsPrefix="-">
<MoreHorizontal />
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item onClick={() => navigate("/buildings/edit")}>
Edit Building
</Dropdown.Item>
<React.Fragment key="deactivateBuilding">
<Dropdown.Item onClick={() => toggle("deactivateBuilding")}>
Deactivate Building
</Dropdown.Item>
<Modal
show={openModals["deactivateBuilding"]}
onHide={() => toggle("deactivateBuilding")}
centered
>
<Modal.Header closeButton>
<b>Admin Function:</b> Deactivate Building
</Modal.Header>
<Modal.Body className="m-3">
<p className="text-left mb-0">
Are you sure you want to deactivate the
<b>Bus Works</b> building? This will prevent the
building from showing up in the platform completely.
</p>
</Modal.Body>
<Modal.Footer>
<Button
variant="secondary"
onClick={() => toggle("deactivateBuilding")}
>
Close
</Button>{" "}
<Button
variant="danger"
onClick={() => {
toggle("deactivateBuilding");
notyf.open({
type,
message: "The building has been deactivated.",
duration,
ripple,
dismissible,
position: {
x: positionX,
y: positionY,
},
});
}}
>
Deactivate Building
</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
</Dropdown.Menu>
</Dropdown>
</div>
</IsAllowed>
<h1 className="mb-0 pb-0">{name}</h1>
<Badge
className={
status === "Live Building"
? "my-2 btn-gradient inline"
: "my-2 inline"
}
bg="success"
>
{status}
</Badge>
</Card.Title>
</Card.Header>
<Card.Body>
<h5>Building Overview:</h5>
<p className="mb-4">{description}</p>
<div className="row">
<div className="col-md-4">
<div className="mb-4">
<h5>Created Date</h5>
<p>{created}</p>
</div>
</div>
<div className="col-md-4">
<div className="mb-4">
<h5>Go-Live Date</h5>
<p>{golive}</p>
</div>
</div>
<div className="col-md-4">
<div className="mb-4">
<h5>Key Contacts</h5>
<div>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar3}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar2}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<span className="me-1">
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="tooltip-right">Joe Bloggs</Tooltip>
}
>
<img
src={avatar1}
width="28"
height="28"
className="rounded-circle me-2"
alt="Joe Bloggs"
/>
</OverlayTrigger>
</span>
<Link to="#" className="d-inline-block text-muted fw-bold ms-2">
+2 more
</Link>
</div>
</div>
</div>
</div>
</Card.Body>
</Card>
);
};
I figured this out and wanted to share my answer in-case it helps anyone else.
It turns out that because I am trying to access the nested object property in the rendered component before the API call has finished, the property ("stats") doesn't exist. When the state eventually updates once the API call has finished, it cannot update the component resulting in the error I was seeing.
The way to fix this is to do something like this:
.... // useState should be set to an object, not an array
const [buildingData, setBuildingData] = useState({});
.... // skipping past intermediary code for brevity
.then(function (response) {
if (isMounted) {
setBuildingData({
name: response.data[0].building_name,
status: response.data[0].status,
description: response.data[0].description,
keycontacts: response.data[0].key_contacts,
created: response.data[0].stats.created_date,
golive: response.data[0].stats.golive_date
});
}
})
.... // then to access it in the component use:
<BuildingProfile
name={buildingData.name}
status={buildingData.status}
description={buildingData.description}
keycontacts={buildingData.keycontacts}
created={buildingData.created}
golive={buildingData.golive}
/>
Doing it this way means that the nested object is updated with API data inside of the useEffect hook, not in the component itself.
Hope this helps somebody.

Passing click function from One component to other component

In React material ui i am having two components where i am calling save function on button click, is it right way or not can anyone suggest the better way:
const callback = {};
return (
<>
{!state?.creditCard?.isSaved ? (
<Paper elevation={4} className={classes.paymentContainer}>
<Box className={classes.subPaymentContainer}>
<Typography className={classes.title}>Card Payment</Typography>
<CardPaymentForm
callback={callback}
validationPassed={() => actionsCollection.booking.saveCard(true, state.creditCard.lastFourDigits)}
formType="profileForm"
/>
<div>
<Button
type="submit"
onClick={(e) => callback.saveCard(e)}
value="SAVE CREDIT CARD"
className={classes.button}
/>
<div style={{ display: "flex", marginTop: 20 }}>
<img className={classes.lockIcon} src={lockIconInfo} alt="" />
<Typography className={classes.paymentInfo}>
<Link href="/terms" target={"_blank"}>
Terms of Payment
</Link>
.
</Typography>
</div>
</div>
</Box>
</Paper>
) : (
<div style={{ height: 373 }}>
<CardStored removeCard={removeCard} />
</div>
)}
</>
);
in CardPayementForm below calling the save function below is the code:
const CardPaymentForm = ({ classes, callback, validationPassed, formType, lastFourDigits }) {
useEffect(() => {
callback.saveCard = (e) => {
e.preventDefault();
=
if (validateForm()) {
=
validationPassed();
}
};
});
}
here without callback how to call save function directly in cardpaymentform, Any help please
I'm not sure this will apply to your problem but if you had a component_a
like
const ComponentA = ({handleClick}) => {
return(
<>
<button onClick(e => handleEvent(e))>
Click here
</button>
</>
}
and a component_b
const ComponentB = () => {
const handleClick = (e) => {
// do something with the event from component a
}
return(
<>
<ComponentA handleClick={handleClick}/>
</>
)
}

react bootstrap offcanvas custom close button

I got offcanvas working on react using bootstrap. Now I want add a custom close button but it doesn't seems to work. Please guide me on what I'm doing wrong.
<Offcanvas
show={show}
placement="bottom"
onHide={handleClose}
{...props}
className={css["offcanvas-bottom"]}
>
<Offcanvas.Header className="p-0">
<button type="button" class="btn-close text-reset new_close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
<div className={css["pop-image"]}>
<Image
src="https://xxxxxxx/2022030117344459.jpg"
fluid
/>
</div>
</Offcanvas.Header>
<Offcanvas.Body>
Some text as placeholder. In real life you can have the elements you
have chosen. Like, text, images, lists, etc.
</Offcanvas.Body>
</Offcanvas>
I used onClick in my custom button. And toggled the state by doing
const toggleOffcanvas = () => setShow(!show)
Example:
function ToggleSidebarOffcanvas(){
const [show, setShow] = useState(false);
const toggleOffcanvas = () => {
setShow(!show);
};
return (
<Button onClick={toggleOffcanvas}>
Menu
<SidebarOffcanvas show={show} toggleOffcanvas={toggleOffcanvas} />
</Button>)
}
function SidebarOffcanvas({ show, toggleOffcanvas }) {
return (
<Offcanvas className="w-25" show={show} scroll={true} backdrop={false}>
<Offcanvas.Header
className="p-0"
style={{
backgroundColor: "#008069",
color: "white",
}}
>
<Offcanvas.Title>
<div
className="d-flex align-items-end w-100 mb-2 lh-1"
>
<div className="p-2" onClick={toggleOffcanvas}>
<FiArrowLeft />
</div>
<h5 className="ms-3">Profile </h5>
</div>
</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<div >
...
</div>
</Offcanvas.Body>
</Offcanvas>
);
}

React Js Multiple Modal

Hi i'm trying to learn react and i was tryign to make multiple modal but the same desgine and everything but different content inside it how?
import './Works.css';
import React, { useState } from 'react'
import Modal from '../Modal/Modal'
function Works(props){
const [isOpen, setIsOpen] = useState(false)
return(
<div>
<div className={props.NameClass}>
<h1>{props.icon}</h1>
<h1>{props.title}</h1>
<p >simple pargraph</p>
<button className="button_more" onClick={() => setIsOpen(true)}><i className="fas fa-caret-right"></i></button>
</div>
<Modal open={isOpen} title="hi" onClose={() => setIsOpen(false)}>
</Modal>
</div>
)
}
export default Works
and if you look at <Modal open={isOpen} title="here i wanna different title" onClose={() => setIsOpen(false)}>
and here the App.js calling the file on top multiple times but i wanna for example if someone click on the second project it change the title to for example "hi you're in the second modal!"
<div class="Div-Projects">
<Works NameClass="First_Project" icon="💳" title="first" />
<Works NameClass="Second_Project" icon="🎓" title="second" />
<Works NameClass="Third_Project" icon="👩🏻‍💻" title="third" />
</div>
and here the modal code!
import './Modal.css'
function Modal({open , title, onClose}){
if(!open) return null
return(
<div className="popup">
<div className="content">
<h1>{title}</h1>
<div className="p">
<p>login system for students attendees with an arduino and python it shows how fast and stable for doing the job.login system for students attendees with an arduino and python it shows how fast and stable for doing the job.login system for students attendees with an arduino and python it shows how fast and stable for doing the job.</p>
</div>
<div className="buttons">
<button className="dismiss" onClick={onClose}>Dismiss!</button>
<button className="github" onClick={() => window.open( 'https://github.com/Majiedo/Login_System')}><i className="fab fa-github"></i></button>
<button className="code"><i className="fas fa-code"></i></button>
</div>
</div>
</div>
)
}
export default Modal
Why don't you pass Works props to Modal component? Like this:
function Works(props){
const [isOpen, setIsOpen] = useState(false)
return(
<div>
<div className={props.NameClass}>
<h1>{props.icon}</h1>
<h1>{props.title}</h1>
<p >simple pargraph</p>
<button className="button_more" onClick={() => setIsOpen(true)}><i className="fas fa-caret-right"></i></button>
</div>
<Modal open={isOpen} title={`hi you're in the ${props.title} modal!`} onClose={() => setIsOpen(false)}/>
</div>
)
}
You need to pass in title {props.title}
change it:
<Modal open={isOpen} title="hi" onClose={() => setIsOpen(false)}>
</Modal>
On this:
<Modal open={isOpen} title={`hi you're in the ${props.title}`} onClose={() => setIsOpen(false)}/>

How to make links work with card-img-overlay React

I'm having an issue on my project. I created a card-img-overlay to display icons over an image. If you click on the entire image you are redirected to a post. I would like to make the like and share icons clickable.
My project is in Reactjs. I am displaying images and videos from Reddit API.
Thank you for your help.
id,
slugTitle,
title,
url_overridden_by_dest,
author,
preview,
}) => {
const [isVideo, setIsVideo] = useState(false);
useEffect(() => {
if (preview) setIsVideo(preview.split('.').pop() === 'mp4');
}, [preview]);
const history = useHistory();
const goToPage = () => {
history.push(`/Post/${id}/${slugTitle}`);
};
return (
<Card
inverse
onClick={goToPage}
style={{
cursor: 'pointer',
}}
>
{isVideo && (
<video autoPlay="false" loop width="100%" src={preview}>
<track default kind="captions" />
</video>
)}
{!isVideo && (
<CardImg top width="100%" src={url_overridden_by_dest} alt={title} />
)}
<CardImgOverlay className="hideinfos">
<CardText className="w-100 d-flex justify-content-between">
<div>
<VscAccount className="mr-2" size={20} />
{author}
</div>
<div>
<LikeButtonhp
className="mr-2 card-link"
size={20}
style={{
position: 'relative',
}}
/>
<BiShareAlt size={20} />
</div>
</CardText>
</CardImgOverlay>
</Card>
);
};
You'll need to put onClick handlers on your LikeButtonhp and BiShareAlt components, and use event.stopPropagation() to stop the event from bubbling up to the <Card />:
<BiShareAlt
size={20}
onClick={event => {
event.stopPropagation();
// Do stuff for share click
}}
/>
You may need to alter the BiShareAlt and LikeButtonhp components to support an onClick prop also, for example if they render a <button> element it may look like this:
const BiShareAlt = ({ onClick }) => (
<button onClick={onClick}>
Share
</button>
);
export default BiShareAlt;
In my onClick, I added an e.stopPropagation(); and it solves my problem. Now I can click on the heart icon and it works. It stops the onClick set up on my image (parent).
function LikeButtonhp() {
const [liked, setLiked] = useState(false);
return (
<Button
outline
color="link"
className="likebutton"
onClick={(e) => {
e.stopPropagation();
setLiked(!liked);
}}
style={{ color: 'white' }}
>
{liked ? <BsHeartFill size={20} /> : <BsHeart size={20} />}
</Button>
);
}

Resources