I am trying to show a dialog box based on the data returned from apollo hook, where I would have to check that one of the values matches an id.
When checker===true I want the dialog to open on render and when the user clicks the Close button, the dialog should close.
const DialogComponent = () => {
const {data, loading, error} = useQuery(GET_QUERY_DATA)
const [isDialogOpen, setIsDialogOpen] = useState(false);
const checker = data && data.getData.some((item_data.id === id))
const closeDialog = () => {
setIsDialogOpen(false)
}
if(checker) {
setIsDialogOpen(true)
}
return(
<Dialog
open={isDialogOpen}
close={closeDialog}>
// dialog content here
<DialogActions>
<Button onClick={closeDialog}> Close </Button>
</DialogActions>
</Dialog>
)}
The above errors with too many re-renders.
I have tried a conditional render instead however, seems that the Dialog component never opens even when checker===true (below).
const DialogComponent = () => {
const {data, loading, error} = useQuery(GET_QUERY_DATA)
const [isDialogOpen, setIsDialogOpen] = useState(false);
const checker = data && data.getData.some((item_data.id === id))
const closeDialog = () => {
setIsDialogOpen(false)
}
if(checker) {
setIsDialogOpen(true)
}
return(
{checker && <Dialog
open={isDialogOpen}
close={closeDialog}>
// dialog content here
<DialogActions>
<Button onClick={closeDialog}> Close </Button>
</DialogActions>
</Dialog>
)}}
I have also tried replacing the open prop value with checker I.e. open={checker} however, then the Dialog box never can be closed even when clicking the Close button.
Any help appreciated.
The close button does close the dialog, it is being opened again on the next render with
if(checker) {
setIsDialogOpen(true)
}
you could do:
<Dialog
open={isDialogOpen && checker}
close={closeDialog}>
<DialogActions>
<Button onClick={closeDialog}> Close </Button>
</DialogActions>
</Dialog>
One problem I see in your code is regarding this part:
if (checker) {
setIsDialogOpen(true)
}
Every time a state is updated in a component, the component funcion is called again to re-render it with the updated state. So the snippet above is executed again and if checker is true, the state is updated again, then it keeps re-redering again and again.
Try wrapping the snippet above inside a React.useEffet() like this:
React.useEffect(() => {
setIsDialogOpen(checker)
}, [checker])
Related
The problem is...
The first popup renders fine.
But when I try to render the second popup, it's not working.
A new popup is not invoked, the previous popup is refreshed.
I want to call a new popup when I clicked a cell in the grid.
my code is like this
const Main = () => {
const [isPopupOpen, setIsPopupOpen] = useState(false);
return (
<>
... other components (including grid)
{ isPopupOpen && <Popup />}
</>
)
};
when Grid is Clicked, 'isPopupOpen' is updated to true.
I use 'react-new-window' library, and this library use 'window.open()' ((https://github.com/rmariuzzo/react-new-window)
so I set different window names to call several popups.
but I can't solve the problem.
I try to set a state object that has a boolean value.
const [popupObj, setPopupObj] = useState({});
when the grid is clicked, popupObj updates like
{'cellA': true, 'cellD': true}
and a return statement is like
{popupObj[cellName] && <Popup /> }
but the result was the same.
what should I do to solve this problem?
I wrote an example for you. Hope it helps.
use popupIds state to store the popups that you want to open
use Set to toggle the popupIds in the addPopup click handler
import * as React from "react";
export default function App() {
const [popupIds, setPopupIds] = React.useState([]);
const addPopup = (popupId) => {
const set = new Set(popupIds);
if (set.has(popupId)) {
set.delete(popupId);
} else {
set.add(popupId);
}
setPopupIds(Array.from(set));
};
return (
<div className="App">
{["hello", "react"].map((popupId) => (
<div onClick={() => addPopup(popupId)}>{popupId}</div>
))}
{popupIds.map((popupId) => (
<Popup title={getPopupTitle(popupId)} />
))}
</div>
);
}
const getPopupTitle = (popupId) => `title for ${popupId}`;
const Popup = ({ title }) => <div>{title}</div>;
Here is a codesandbox that you can play with directly.
You need to add your popup in an array, so you can render many popup as you want, then you need to define in How much time you will remove a added popup from array or add a close button
Extra: you can configure in global state to access in all your application to your popups and you will have a component like this: https://www.npmjs.com/package/notistack
I'm prototyping a new version of our application in React 18. I'm somewhat new to React and have stumbled upon a scenario that has a few different problems.
We need to open a modal/dialog when a user performs an action. They will click a button to edit data, that opens a dialog window with a form. When they close the dialog, the form data is passed back to the component which opened it.
In our old app, it would be something like const user = new UserModal(123)
I'm using BlueprintJS's Dialog component for this, but this issue is applicable to any library.
I'm writing a wrapper because all of our modals will have similar functionality so the props to the Dialog component will never change outside of whether it's open or not.
Here's a super basic example of this "wrapper" component:
export const Modal = ({ isOpen }: ModalProps) => {
const [isOpen2, setIsOpen] = useState(isOpen);
const handleClose = useCallback(() => {
setIsOpen(false);
}, []);
return (
<Dialog isOpen={isOpen2}>
<p>this is a dialog</p>
<Button onClick={handleClose} text="close" />
</Dialog>
);
}
Using this in the parent would look like this:
const Demo = () => {
const [isOpen, setIsOpen] = useState(false);
// some code calls setIsOpen(true) when we need to open the modal
return <Modal isOpen={isOpen} />;
}
This presents multiple problems:
A parent controller can trigger this dialog to open, but never close (interacting with the app is prevented while a modal is open)
The modal can close itself via an X or "Cancel" button
This leads to two useState invocations - one in the parent controller and one inside the modal. This doesn't work right by itself, because once the state is set in the controller, it can't update when the prop changes with more code
The parent controller would need to know when it closes so it can update it's own state value.
I really dislike having to put <Modal> elements in the parent jsx, I liked the new UserModal code but that might be a fact of life
Overall, this feels like a very wrong approach. How can I design this to be more "proper" and yet work the way I need?
you can pass your method from parent to child and call there and also you can use 1 state for manage modal status.
export const Modal = ({ isOpen, handleClose, closeCallBack }: ModalProps) => {
const handleCloseChild = () =>{
closeCallBack()
handleClose()
}
return (
<Dialog isOpen={isOpen}>
<p>this is a dialog</p>
<Button onClick={handleCloseChild} text="close" />
</Dialog>
);
}
and parent something like this
const Demo = () => {
const [isOpen, setIsOpen] = useState(false);
// some code calls setIsOpen(true) when we need to open the modal
const handleClose = () =>{
setIsOpen(false)
}
return <Modal isOpen={isOpen} handleClose={handleClose} closeCallBack={() => // do what you want on close modal or you just do this in side modal or even in handelClose function } />;
}
I am trying to show a message when user try to leave current page, so I am using history.block like this:
import { useHistory } from "react-router-dom";
const ProfilerCreate = ({ pageType }) => {
const history = useHistory();
const [isDisabled, setIsDisabled] = useState(true);
const [openModalUnsave, setOpenModalUnsave] = useState(false);
useEffect(() => {
history.block(validateChange);
}, []
);
//Function to validate changes and open modal
function validateChange(txt) {
if (!isDisabled) {
toggleModalUnsave();
return false;
}
}
//Function to open or close modal
function toggleModalUnsave() {
setOpenModalUnsave(!openModalUnsave);
}
//Function to return landing page
function returnPage() {
history.push("/");
}
return (
...
<div style={{ display: "none" }}>
<Modal
id="myModal"
heading="You have unsaved changes"
description="Do you want to save or discard them?"
isOpen={openModalUnsave}
onRequestClose={(detail) => toggleModalUnsave()}
actionsRight={
<>
<Button display="text" onClick={() => returnPage()}>
Discard
</Button>
<Button
display="primary"
onClick={(evt) => saveAudienceData(evt)}
>
Save and exit
</Button>
</>
}
>
<p>Modal Children</p>
</Modal>
</div>
);
export default ProfilerCreate;
when it is detecting unsaved changes, it shows a modal with a warning and two buttons, one for save and the other for discard, when the user hit discard button it should return to home page, but history.push is not working.
I tried to find the solution or I don't know if I am using the history.block in a wrong way.
I hope that you can help me, thanks!
I think you are missing the unblock() method in validateChange(txt)
I have a modal that is completely self contained. The modal is opened via going to the modal route and all the functionality to close the modal from button or outside clicks is within the modal component. Basically the modal is not controlled by any parent passing state. I was given a task of making the modals button customizable, meaning passing in a new button component, so we can add the modal to our lib instead of copy pasting the code in projects. Lol this seemed simple enough, and maybe it is and I am just overthinking this.
I cant paste the actual code but I can use a contrived example. This is a very simplified version of the modal, keeping in mind it opens via route so there's really no state and setState in the actual code. Also here is a fiddle
const ModalHeader = ({ onClose }) => {
return (
<div className="modal__header">
<button
className="modal__close-btn"
data-testid="modal-close-button"
onClick={onClose}
/>
</div>
);
};
const Modal = ({ children }) => {
const [state, setState] = React.useState(true);
const handleCloseOutsideClick = () => {
setState(false);
};
const handleCloseButtonClick = () => {
setState(false);
};
const renderModal = () => {
return (
<div className="modal-overlay" onClick={handleCloseOutsideClick}>
<div className="modal">
<ModalHeader onClose={handleCloseButtonClick} />
{children}
</div>
</div>
);
};
return state ? renderModal() : null;
};
const App = () => {
return (
<Modal>
<div>Modal Children</div>
</Modal>
);
};
ReactDOM.render(<App />, document.querySelector('#app'));
I tried a few things, initially I attempted to find a way to pass in a new header component containing a button. Then as I got into the code I realized what I was doing would lose the self contained functionality of the modal. My approach was along the lines of below but obviously the onClick would be an issue since invoking the close functionality is internal.
So I tried using cloneElement to add props within the component if the custom header was detected:
// inside modal component
React.useEffect(() => {
React.Children.map(children, (child: React.ReactElement) => {
if (child && child.type === ModalHeader) {
setHederFound(true);
}
});
}, []);
// inside modal render:
<div className={modalClasses} onClick={stopPropagation}>
{!headerFound ? (
<ModalDefaultHeader onClose={handleCloseButtonClick} />
) : (
React.Children.map(children, (child: React.ReactElement) => {
if (child && child.type === ModalHeader) {
return React.cloneElement(child, {
onClose: handleCloseButtonClick,
});
}
})
)}
{children}
</div>;
Obviously that did not work because there's no onClick in the custom button. Anyways I am thinking that I am over complicating this. I just need a way to pass in a custom button while leaving the functionality internal to the modal. Any assistance would be appreciated.
Thanks in advance.
I am trying to open a Modal with a self-created button in React and Material-UI. The button also envokes other funcstions, that is why I need to integrate the "Modal Opening" and the other function within one button. Currently, the modal will not open automatically when clicking on the button.
Do you have any ideas?
This is how I integrated the "CanvasLoadingModal" into the Parent Component:
Step 1. Create a Button that envokes a certain function
return(
<div>
<Button
variant="contained"
fullWidth
className={button1}
onClick={saveButtonHandler}
>
Chatbot in Datenbank <br /> speichern
</Button>
</div>
Step 2. Include the "CanvasLoadingModal" with props = {true} in "saveButtonHandler" function
const saveButtonHandler1 = () => {
const showLoadingModal = () => {
return <CanvasLoadingModal open={true} />;
};
showLoadingModal();
}
You can update the state and let React handle the rendering part and conditionally render the CanvasLoadingModal component when clicked. The same function can also be used to close the modal as well.
const App = ()=> {
const [showModal,setShowModal] = useState(false);
function showModalHandler(){
setShowModal(!showModal);
}
return (
<Button onClick={showModalHandler} />
{showModal ? <CanvasLoadModal open={showModal} />: null }
)
}