Function call and pass value in the same OnClick - React - reactjs

I got a OnClick which actually receives an id:
<Button onClick={() => addToCart(id)} >Buy</Button>
On the other hand, in a different JS file,I got a modal which appears with a click via useState:
const [stateModal1, changeModalState1] = useState(false);
Now, in the same component I work with this modal, I map an array which returns a Button, which now is working with the "addToCart(id)" value mentioned before, like this:
{products.map((product) => {
return <Product image={product.image}
key={product.id}
data={product}
addToCart={() =>addToCart(product.id)} />})}
The question that is driving me crazy is: how can I use the button in the mapped array to trigger that modal, and at the same time, to pass values to that modal in order to show the mapped item IN that modal?
Thanks in advance.
EDIT: this is the modal, which is another component:
const Modal = ({
children,
state,
stateModal1,
})
return (
<>
{state &&
<Overlay>
<Container>
<CloseButton onClick={() => changeState(false)}>{FaWindowClose}</CloseButton >
{children}
<Header>
<h3>Confirm buy</h3>
<h4>{name}</h4>
<h4>$ {price}</h4>
</Header>
<Button onClick={() => changeState(false)}>Confirm</Button>
</Container>
</Overlay>
}
</>)
PS: the "confirm" button which triggers the "changeState()", should also trigger the addToCart().

As mentioned by other comments above, you can pass a prop to the modal component from the parent component to achieve your demand normally.
The only thing that needs to be done is set the open/close modal state and the passing data state at the same time, or, probably use one state directly
sample of the code:
import "./styles.css";
import "antd/dist/antd.css";
import { useState } from "react";
import { Modal } from "antd";
export default function App() {
// init with undefined, if not undefined, open the modal
const [modal, setModal] = useState(undefined);
const list = [...Array(20).keys()];
// set the state to open the modal, as well as pass it to the modal itself as a prop if necessary
const handleClick = (idx) => () => {
setModal(idx);
};
return (
<div className="App">
{list.map((x, idx) => (
<div style={{ border: "1px solid black" }} onClick={handleClick(idx)}>
{x}
</div>
))}
<Modal visible={modal !== undefined}>The value you passed: {modal}</Modal>
</div>
);
}
the online demo could be found here: https://codesandbox.io/s/hardcore-shape-89y78?file=/src/App.js

Related

Close modal if another one is opened

Im trying to create a react Modal component from scratch. I would like to add the functionality of closing the modal when another one is opened.
I know the logic how to solve(i think i know), but cant implement it. My approach would be using context, where i store the current modal opened(currentModal) and if another one is opened then it would check if there is a currentModal and if so it would close it.
So far i have the modal component:
export function Modal({title, isOpen, children, onClose}){
return(
createPortal(
<trds-modal class={isOpen ? 'opened': ''} onClick={onClose}>
<trds-modal_container onClick={e => e.stopPropagation()}>
<trds-modal_header>
<h2>{title}</h2>
<Icon icon="x" onClick={onClose} />
</trds-modal_header>
<trds-modal_body>
{children}
</trds-modal_body>
</trds-modal_container>
</trds-modal>, document.body)
)
}
i figured it out.
Created a context provider where i store the id of the current modal opened.
export function ModalContextProvider({children}){
const [currentModalId, setCurrentModalId] = useState(null);
return(
<modalContext.Provider value={[currentModalId, setCurrentModalId]}>
{children}
</modalContext.Provider>
)
}
then in the modal component i generate a uniqe id and set the context's currentModalId to that. And if the currentModalId changes then the modal checks if that equals to the modalId. If not, it calls the onClose function.
export function Modal({title, isOpen, children, onClose}){
const modalId = useMemo(() => generateId(), []);
const [currentModalId, setCurrentModalId] = useContext(modalContext);
useEffect(() => {
if(isOpen){
setCurrentModalId(modalId);
}
}, [isOpen, setCurrentModalId, modalId]);
useEffect(() => {
if(currentModalId !== modalId) onClose();
}, [currentModalId, modalId, onClose]);
return(
createPortal(
<trds-modal class={isOpen ? 'opened': ''} onClick={onClose}>
<trds-modal_container onClick={e => e.stopPropagation()}>
<trds-modal_header>
<h2>{title}</h2>
<Icon icon="x" onClick={onClose} />
</trds-modal_header>
<trds-modal_body>
{children}
</trds-modal_body>
</trds-modal_container>
</trds-modal>, document.body)
)
}
I hope it help you as i couldn't find an approach to this problem. (maybe its not even an existing problem :D)

All accordions collapsed at the same time when I click on any one

I am creating accordions with reactstrap but when I wanna open one accordion all accordions collapsed
I wanna every accordion work separately
my code that I created 2 components in it
const SellerShipping = () => {
const [open, setOpen] = useState(null);
const [toggleChecked, setToggleChecked] = useState(false)
return (
<Wrapper>
<TagLine>Shipping and delivery</TagLine>
<SearchBar>
<SearchBox />
<SuggestButton>suggest a company</SuggestButton>
</SearchBar>
<AccordionWrapper className=''>
<CompanyAccordion toggleChecked={toggleChecked} setToggleChecked={setToggleChecked} open={open} setOpen={setOpen} toggleid={"1"} />
<CompanyAccordion toggleChecked={toggleChecked} setToggleChecked={setToggleChecked} open={open} setOpen={setOpen} toggleid={"2"} />
</AccordionWrapper>
</Wrapper>
)
}
and even if I write states inside the accordion file its the same result
the company accordion
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from 'reactstrap';
import styled from 'styled-components';
import ReactSwitch from 'react-switch';
import { ReactComponent as EmiratesPost } from 'assets/img/logos/emirates-post.svg'
const CompanyAccordion = ({open, setOpen, toggleid, toggleChecked, setToggleChecked}) => {
const toggle = (id) => {
open === id ? setOpen() : setOpen(id);
console.log(id,open);
};
return (
<Wrapper>
<SubWrapper>
<EmiratesPost style={{maxWidth:"100%"}} />
<SwitchComponent>
<ReactSwitch onChange={()=>setToggleChecked(!toggleChecked)} checked={toggleChecked}/>
</SwitchComponent>
</SubWrapper>
<AccordionWrapper>
<Accordion open={open} toggle={toggle}>
<AccordionItem style={{border:"0px"}} >
<AccordionHeader targetId={toggleid}>
Accordion Item 1
</AccordionHeader>
<AccordionBody accordionId={toggleid}>
<strong>This is the first item's accordion body.</strong>
You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow.
</AccordionBody>
</AccordionItem >
</Accordion>
</AccordionWrapper>
</Wrapper>
);
}
the result
the result
you have used the same state toggleChecked for all Accordions so they are behaving identically use different states of all the Accordions you wish to have separate behaviour

How can i get multiple recoil atoms when using components multiple?

In some components i am using recoil atoms to manage my states. One example is my modal component. It look something like this:
export const modalState = atom({
key: "modalState",
default: false
})
export const useToggleModalState = () => {
const setModalState = useSetRecoilState(modalState)
return (state, callback) => {
setModalState(state)
if (callback) {
callback()
}
}
}
export const Modal = (props) => {
<Transition show={modalState}>
<Dialog>
<Dialog.Title>My Modal Headline</Dialog.title>
<Dialog.Description>My Modal Description</Dialog.Description>
</Dialog>
</Transition>
}
and i am using this modal like this:
const toggleModalState = useToggleModalState();
return (
<Modal />
<Button text="Close Modal" onClick={() => toggleModalState(false)} />
)
however, if I use the modal multiple times, the modal is automatically duplicated, but I still use only one state for all modals. Of course, I don't want that. If I use a component multiple times, I want the state to be created multiple times, so that I can change the state of each component individually.
I have read that there are also atomFamilys. Could I use these at this point? What should my code look like then? Can multiple atoms also be created automatically if I use a component multiple times?
Why do you want to use recoil for that? The state of the modal is tied to the modal itself, it doesn't need to access global state.
You can just use useState to determine if you want to show a modal within a component:
export const Modal = (props) => {
<Transition show={props.show}>
<Dialog>
<Dialog.Title>My Modal Headline</Dialog.title>
<Dialog.Description>My Modal Description</Dialog.Description>
</Dialog>
</Transition>
}
export const ComponentWithModal = () => {
const [showModal, setShowModal] = useState(false);
return (
<>
<Modal show={showModal}/>
<Button text="Open Modal" onClick={() => setShowModal(true)} />
<Button text="Close Modal" onClick={() => setShowModal(false)} />
</>
)
}

Call a material ui dialog

How to call a Material UI Dialog during onClick on delete icon ie onClick={deletePlayer(id)} ?
I have added the Dialog.js under modal/Dialog and imported to Home component.
I have added a demo here
Short answer: Forked CodeSandbox with working dialog
Long answer:
First of all, you need to move the display/dismiss logic out of the AlertDialog component and into the component that actually triggers the display of the modal (in your case, the Home component). This means that you'll receive the open state and onClose handler as props (along with the playerId which will hold the ID of the player being targeted for deletion). So the signature of your dialog component becomes:
export default function AlertDialog({ open, onClose, playerId }) {
return (
<Dialog open={open} onClose={onClose} ...> ... </Dialog>
);
}
In Home, we add the logic to track and set the state of both the dialog open/closed status, and the ID of the player targeted for deletion. We do this through useState:
const [deleteDialog, setDeleteDialog] = useState(false);
const [playerId, setPlayerId] = useState("");
While you could have as many AlertDialog components as you have players by adding <AlertDialog /> inside your player map loop, it is redundant as you'll only ever have one modal active (by definition). So all you have to do is place a single instance of <AlertDialog /> in your Home component. A good convention is to place it before the closing encompassing tag:
return (
<div className="App">
.
.
.
<AlertDialog
open={deleteDialog}
onClose={() => setDeleteDialog(false)}
playerId={playerId}
/>
</div>
);
Finally, we deal with the handler responsible for displaying the modal, in your case deletePlayer. We have two things to do there: set the player ID targeted for deletion through the playerId state variable, and display the modal through the deleteDialog state variable:
const deletePlayer = id => e => {
setPlayerId(id);
setDeleteDialog(true);
};
Create a state in the Home component to handle the Dialog visibility, set the state on click and render the AlertDialog conditionally:
const [openDialog, setOpenDialog] = useState(false);
...
const deletePlayer = id => e => {
setOpenDialog(true);
};
...
return(
...
{openDialog && (
<AlertDialog isOpen={openDialog} setIsOpen={setOpenDialog} />
)}
Then in the AlertDialog component:
export default function AlertDialog(props) {
const { isOpen, setIsOpen } = props;
const handleClose = () => {
setIsOpen(false);
};
return (
<div>
<Dialog
open={isOpen}
onClose={handleClose}
...
Working Example:

How to dismiss a React Modal using createPortal()?

Trying to dismiss a Modal dialog from within the Modal. I'm using ReactDOM.createPortal().
index.html
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<div id="modal"></div>
</body>
ResponseModal.js
import React from "react";
import ReactDOM from "react-dom";
// The gray background
const backdropStyle = {
...
};
// The modal "window"
const modalStyle = {
...
};
const MODAL_ROOT = document.querySelector("#modal");
const JSX_MODAL = ({ children, setShowResponses }) => (
<div style={backdropStyle}>
<div style={modalStyle}>
Child: {children}
<br />
<input
type="button"
value="Dismiss"
onClick={() => {
setShowResponses(false);
}}
/>
</div>
</div>
);
function Modal({ showResponses, ...props }) {
console.log("floop", showResponses);
if (showResponses) {
return ReactDOM.createPortal(JSX_MODAL(props), MODAL_ROOT);
}
return null;
}
export default Modal;
And finally, the containing component:
const LargeCell = ({ SCID, extra, fen, color }) => {
const [showResponses, setShowResponses] = useState(false);
return (
<div
style={{
...
}}
onClick={() => setShowResponses(true)}
>
<SmallCell {...{ SCID, color }} />
<DiagramForCell {...{ fen }} padding="3em"></DiagramForCell>
<span className="diff-text opening-text">{extra.opening.desc}</span>
<ResponsesModal {...{ showResponses, setShowResponses }}>
FLUM!
</ResponsesModal>
</div>
);
};
When I click on the LargeCell div, I see:
However, the dismiss button doesn't work. I'm sure that setShowResponses(false) is called, but there is no re-rendering of the Modal, so it is not dismissed. If I look at the Modal component in Chrome devtools, the state of showResponses still shows true.
So the question is: what is the correct way to dismiss this Modal?
So LargeCell was a table cell component, of which there were multiple. What I did was to push the Modal to the table level, and show/hide it from there:
{showResponses ? (
<ResponsesModal {...{ setShowResponses }}>FLUM!</ResponsesModal>
) : null}
setShowResponses is called by the Dismiss button in the Modal as shown previously.
The disadvantage is that to bring up the modal, each LargeCell needs setShowResponses, also. That prop has to be pushed several levels down. That's okay for now, but I'm starting to wonder if I should use a context.

Resources