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:
Related
Need modal inside modal if you click on edit then is one modal is opening. I need one more modal inside that opened modal
To create nested modal popups in React you can use a combination of modal components and conditional rendering.
Example
import React from 'react';
const ParentModal = () => {
const [showChildModal, setShowChildModal] = React.useState(false);
return (
<>
<button onClick={() => setShowChildModal(true)}>Show Child Modal</button>
{showChildModal && <ChildModal onClose={() => setShowChildModal(false)} />}
</>
);
};
const ChildModal = ({ onClose }) => {
const [showGrandchildModal, setShowGrandchildModal] = React.useState(false);
return (
<>
<button onClick={() => setShowGrandchildModal(true)}>Show Grandchild Modal</button>
{showGrandchildModal && <GrandchildModal onClose={() => setShowGrandchildModal(false)} />}
<button onClick={onClose}>Close</button>
</>
);
};
const GrandchildModal = ({ onClose }) => (
<>
<p>I'm the grandchild modal!</p>
<button onClick={onClose}>Close</button>
</>
);
export default ParentModal;
In the above example, we have three modal components: ParentModal, ChildModal, and GrandchildModal. The ParentModal component is the root of the nested modal popups, and it contains a button that will show the ChildModal when clicked. The ChildModal component, in turn, contains a button that will show the GrandchildModal when clicked. Each modal component also has a "Close" button that will close the modal when clicked.
We use the useState hook in each component to track the state of the modal (whether it is open or closed), and we use conditional rendering
I am creating a react website in which many pages have a specific button that should look the same for all. Should this button be its own component? If so, how would I specify the onClick events to be different for each button if it is a component?
Yes, it should be its own component.
Create it in a separate file so you can import them where you need to use.
The component should receive a onClick prop that you pass to the internal button tag.
See this example: https://reactjs.org/docs/components-and-props.html#composing-components
export const Button = ({ label, onClick, disabled }) => {
return (
<button
onClick={onClick}
disabled={disabled}
>
{label}
</button>
)
}
and then you can export this component inside any of the components you want, and pass in values like onClick and label to make it more dynamic
export const DemoFunction () => {
const onClickHandler = () => {}
return (
<Button label="Click" onClick={onClickHandler} />
)
}
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
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)} />
</>
)
}
How do I pass a state attribute from parent to child? In the following implementation, the Dropdown component has a state "isActive" and I want to access it in the Button component to attach propper styling to it. The Dropdown has to generic as it is supposed to take different sorts of buttons.
<Dropdown items="...">
<Button active ="false" />
</Dropdown>
Dropdwon.js
...
constructor(props){
super(props)
this.state = {
isActive: true,
}
}
render (){
return (
<div className={styles.toggle} onClick={(event) => this.showMenu(event)}>
{this.props.children} /* want to set active prop for the child button here */
</div>
);
}
...
You have two possibilities:
Lift your Dropdown state and keep it in its parent component;
Use useContext hook;
The first approach would be better, but it may not be good for your application (I cannot know that). Let me make an example for both cases.
This is an example where I've lifted the isActive state to the parent component.
const ParentComponent = () => {
const [isActive, setIsActive] = useState(false);
handleIsActiveChange = (newValue) => {
setIsActive(newValue);
}
<Dropdown isActive={isActive} setIsActive={handleIsActiveChange}>
<Button isActive={isActive} />
</Dropdown>
}
const Dropdown = props => {
// You can use `props.isActive` to know whether the dropdown is active or not.
// You can use `props.handleIsActiveChange` to update the `isActive` state.
}
const Button = props => {
// You can use `props.isActive` to know whether the dropdown is active or not.
}
Instead, this exploits the useContext API:
const dropdownContext = React.createContext(null);
const Dropdown = props => {
const [isActive, setIsActive] = useState(false);
return (
<dropdownContext.Provider value={{ isActive }}>
{props.children}
</dropdownContext.Provider>
);
}
const Button = props => {
const dropdownCtx = React.useContext(dropdownContext);
// You can use `dropdownCtx.isActive` to know whether the dropdown is active or not.
}
Aside from the answer I linked, there might be another way of achieving this that I didn't see mentioned.
You can send a function as a children element of your dropdown which will take isActive as a variable :
<Dropdown items="...">
{isActive => <Button active={isActive} />}
</Dropdown>
Then, is the render function, simply call the function and send your state value as a parameter :
render(){
return (
<div className={styles.toggle} onClick={(event) => this.showMenu(event)}>
{this.props.children(this.state.isActive)}
</div>
);
}
<Dropdown >
<Button isActive={this.state.isActive} />
</Dropdown>
In your button get it with this.props.isActive