I'm attempting to replace browser alerts with Dialog components from MUI in my React project. The code below is taken from a JSX component that will make an API call to delete a comment from a database.
I'm able to get the dialog to work in order to delete the comment and optimistically render the result however, in the catch block of the api call I would like to trigger a new Dialog component render should there be an error with the deletion. I'm doing this by flipping the state of "err" to true within the catch block and setting this to the open prop in the Dialog component, but this 2nd Dialog component never renders and the console.logs always show err to be false in the browser console.
What am I missing here? Thanks!
export default function DeleteCom({ commentId, setComments, setComNum }) {
const [open, setOpen] = useState(false);
const [err, setErr] = useState(false);
let comIndexToDelete = 0;
let deletedCom = {};
const handleClose = () => {
setOpen(false);
};
const handleOpen = () => {
setOpen(true);
};
const handleClick = () => {
setComments((currentComments) => {
comIndexToDelete = currentComments.findIndex(
(comment) => comment.comment_id === commentId
);
deletedCom = currentComments[comIndexToDelete];
setComNum((currentComNum) => currentComNum - 1);
return currentComments.filter(
(comment) => comment.comment_id !== commentId
);
});
deleteComment(commentId).catch(() => {
setComNum((currentComNum) => currentComNum + 1);
// alert("There was a problem deleting your comment. Please try again.")
console.log(1, err)
setComments((currentComments) => {
const comClone = [...currentComments];
comClone.splice(comIndexToDelete, 0, deletedCom);
return comClone;
});
setErr(true);
console.log(2, err)
});
};
return (
<div className="com-delete">
{commentId && (
<IconButton
className="IconButton"
aria-label="delete"
size="large"
onClick={handleOpen}
>
<DeleteTwoToneIcon fontSize="inherit" />
</IconButton>
)}
<Dialog
open={open}
onClose={handleClose}
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you wish to delete this comment?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>No</Button>
<Button onClick={handleClick}>Yes</Button>
</DialogActions>
</Dialog>
<Dialog
open={err}
onClose={() => {setErr(false)}}
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
There was a problem deleting your comment. Please try again.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => {setErr(false)}}>Close</Button>
<Button onClick={handleClick}>Retry</Button>
</DialogActions>
</Dialog>
</div>
);
}
Related
I am struggling to understand how to use React modals (specifically React-Bootstrap) when asynchronous code is involved. I am learning about promises and React at the same time so I hope I am formulating the question correctly.
My goal is to launch an asynchronus function with parameters provided by a modal. This is what I am doing at the moment, it works, but I have the impression that I am not separating concerns.
When a button is clicked the async function simulateImprovedBuilding is launched.
async function simulateImprovedBuilding() {
function selectOsMeasures(measures) {
let selectedOsMeasures = measures;
let scenario = "Improved";
simulateBuilding(scenario, selectedOsMeasures).then((result) => {
console.log("Finally!!!!");
renderMonthlySimulation(result);
});
}
const osMeasuresModalRoot = createRoot(document.getElementById("react_modal"));
osMeasuresModalRoot.render(<OsMeasuresModal action={selectOsMeasures} />);
console.log("Done");
}
The modal is rendered, I make the selection and the component execute selectOsMeasures to update the parameters, launch the long running async process and, when completed renders the results on a chart. This is the component
function osMeasuresModalRoot(props) {
const [show, setShow] = useState(true);
const handleClose = () => {
setShow(false);
let selectedOsMeasures = osMeasures.filter(x => selections[osMeasures.indexOf(x)]);
console.log(selectedOsMeasures);
props.action(selectedOsMeasures);
};
const handleShow = () => setShow(true); // Not used
const [selections, setSelections] = useState([]);
const [osMeasures, setOsMeaaures] = useState([]);
useEffect(() => {
let url = `http://${osServer}/os_measures`;
fetch(url)
.then((response) => response.json())
.then((result) => {
setOsMeaaures(result);
setSelections(Array(result.length).fill(false));
});
}, []);
return (
<>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Select ECMs</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
{osMeasures.map((measure, index) => (
<div key={`${measure}`} className="mb-3">
<Form.Check
type={"checkbox"}
id={`${measure}`}
label={`${measure}`}
onChange={() => {
selections[index] = !selections[index];
console.log(selections);
}}
/>
</div>
))}
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Cancel
</Button>
<Button variant="primary" onClick={handleClose}>
Simulate
</Button>
</Modal.Footer>
</Modal>
</>
);
}
I think the selectOsMeasures function is doing too much. I think I should do something like:
simulateImprovedBuilding()
.then(result => renderMonthlySimulation(result));
But if I do this, the function exits immediately after the React render function and of course result is undefined.
First of all, is my concern valid? If so, how should I refactor my code?
How about increasing the scope of the function and making the result global? It's a bit hacky but is more along the lines of what React should be doing rather than having multiple rendering functions. Then it will automatically switch between the Modal and the graph rendering function when it changes:
function SimulateImprovedBuilding() {
let r_result = undefined;
function OsMeasuresModal(props) {
const [show, setShow] = useState(true);
const handleClose = () => {
setShow(false);
let selectedOsMeasures = osMeasures.filter(x => selections[osMeasures.indexOf(x)]);
console.log(selectedOsMeasures);
props.action(selectedOsMeasures);
};
const handleShow = () => setShow(true); // Not used
const [selections, setSelections] = useState([]);
const [osMeasures, setOsMeaaures] = useState([]);
useEffect(() => {
let url = `http://${osServer}/os_measures`;
fetch(url)
.then((response) => response.json())
.then((result) => {
setOsMeaaures(result);
setSelections(Array(result.length).fill(false));
});
}, []);
return (
<>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Select ECMs</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
{osMeasures.map((measure, index) => (
<div key={`${measure}`} className="mb-3">
<Form.Check
type={"checkbox"}
id={`${measure}`}
label={`${measure}`}
onChange={() => {
selections[index] = !selections[index];
console.log(selections);
}}
/>
</div>
))}
</Form>
</Modal.Body>
<Modal.Footer>
<button onClick={handleClose}>
Cancel
</button>
<button onClick={handleClose}>
Simulate
</button>
</Modal.Footer>
</Modal>
</>
);
}
function selectOsMeasures(measures) {
let selectedOsMeasures = measures;
let scenario = "Improved";
simulateBuilding(scenario, selectedOsMeasures).then(r => {
console.log("Finally!!!!");
r_result = r;
});
}
function Render() {
return (
<>
{r_result===undefined? <OsMeasuresModal action={selectOsMeasures} /> : renderMonthlySimulation(r_result)}
</>
)
}
const osMeasuresModalRoot = createRoot(document.getElementById("react_modal"));
osMeasuresModalRoot.render(<Render/>);
console.log("Done");
}
Note: the renderMonthlySimulation(r_result) function will need to become a react component that returns what you want to render
Same can be done in multiple ways, you could use a 'Context' across all the react functions, or use 'Routing' to switch between things you're rendering. It's much better to have a singular .render.
My issue is that when I submit a comment and the state gets updated, the chat does not update properly. Here's a screenshot of how it looks before commenting and after. I'm not entirely sure why the dots appear, as from my understanding, it should update with the comments, and there are no empty comments in the database.
Edit: Just added the chat return!
Here's my code for the chat component:
const Chat = () => {
const {loggedIn, setLoggedIn} = useContext(LoginContext)
const {loginUsername, setLoginUsername} =
useContext(UsernameContext)
const {tickerValue, setTickerValue} = useContext(TickerValue)
console.log('LOGIN USERNAME:' + loginUsername)
const history = useHistory()
///const value = history.location.state
const searchValue = tickerValue.toUpperCase()
const classes = styles()
const [comments, setComments] = useState([])
const commentSection = []
useEffect(() => {
if (messageDiv) {
messageDiv.scrollIntoView(false)
}
if (searchValue.length > 0) {
console.log('SEARCHVALUE ' + searchValue)
console.log('checking comments')
Axios.get('https://stockliner.herokuapp.com/comment_sections/' + searchValue)
.then((res) => {
const data = res.data
//console.log(data)
commentSection.push(data)
setComments(data)
})
}
},[])
const postComment = () => {
const content = document.getElementById('commentPost').value
console.log(content)
if (loginUsername.length > 0) {
Axios.post("https://stockliner.herokuapp.com/add_comment", {
stock_name: searchValue,
username: loginUsername,
content: content
})
.then((res) => {
Axios.get('https://stockliner.herokuapp.com/comment_sections/' + searchValue)
.then((data) => {
const comm = res.data
setComments(comm)
console.log(comments)
///window.location.reload()
})
console.log(res)
})
} else {
alert('YOU ARE NOT LOGGED IN')
}
}
return(
<div>
<Card elevation={6} className={classes.commentCard}>
<CardHeader align="left" title={searchValue + "'s Comments"}/>
<Divider className={classes.titleDiv}/>
<div className={classes.textPanel} id="messageDiv">
{comments ?
Object.keys(comments).map((key) => {
return(
<div className={classes.row}>
<Paper variant="outlined" className={classes.paperDiv}>
<Typography className={classes.username}variant="body">{comments[key].username}: </Typography>
<Typography className={classes.content}variant="body">{comments[key].content}</Typography>
</Paper>
</div>
)
})
: <p>no comments</p>
}
</div>
<Divider className={classes.postDiv}/>
{loggedIn?
<div className={classes.titleDiv}>
<TextField variant="outlined"
label="Comment" size="small"
id="commentPost"/>
<Button variant="outlined" className=
{classes.button} onClick=
{postComment}>Post</Button>
</div>
:
<div className={classes.postDiv}>
<TextField variant="outlined" disabled
label="Login required" size="small"/>
<Button variant="outlined" className=
{classes.button} disabled>Post</Button>
</div>
}
</Card>
</div>
)
}
export default Chat
in useEffect hook there is a list of variables whoose state change you follow and your list is empty, try adding variable there
I got it! Turns out on the second .then after the postComment function, I was updating my state with setComments(res.data) where it should have been setComment(data.data) because "res" was the response of the first .then and "data" was for the second. Dumb mistake but it took me two days to fix!
Following Situation.
I have a functional Parent Component like this:
function TestAutomationTab() {
const theme = createMuiTheme({
typography: {
htmlFontSize: 10,
useNextVariants: true,
},
});
const [szenarios, setSzenarios] = useState([]);
const [filterSzenario, setFilterSzenario] = useState('ALL');
const [data, setData] = useState([{}]);
const [runAll, setRunAll] = useState(false);
const [runAllButton, setRunAllButton] = useState('RUN ALL');
useEffect(() => {
fetchDistinctSzenarios();
fetchTestfaelle();
}, []);
async function fetchDistinctSzenarios() {
const response = await Api.getDistinctTestautoSzenarios();
setSzenarios(response.data);
setSzenarios(oldState => [...oldState, 'ALLE']);
}
function handleFilterChange(event) {
setFilterSzenario(event.target.value);
fetchTestfaelle();
}
async function fetchTestfaelle() {
const response = await Api.getAllOeTestfaelle();
response.data.forEach((e) => {
e.status = 'wait';
e.errorStatus = '';
e.statusText = '-';
});
setData(response.data);
}
function sendSingleCase(id) {
data.forEach((e) => {
if(e.id === id){
e.status = 'sending';
}
})
}
return (
<React.Fragment>
<MuiThemeProvider theme={theme}>
<div style={styles.gridContainer}>
<Upload />
<TestautomationSzenarioFilter
/>
<DocBridgePieChart />
<div style={styles.uebersicht}>
{filterSzenario.length ? <OeTestfallAccordion
choosenFilter={filterSzenario}
testData={data}
runAll={runAll}
sendSingleCase={sendSingleCase}
/> : <div>Wähle Szenario</div>}
</div>
</div>
</MuiThemeProvider>
</React.Fragment>
);
}
OeTestfallAccordion
function OeTestfallAccordion(props) {
const data = props.testData;
return (
<React.Fragment>
{data.map(e => (<OeTestfall
key={e.id}
szenario={e.szenario}
testid={e.testfallid}
json={e.json}
status={e.status}
runAll={props.runAll}
errorStatus={e.errorStatus}
statusText={e.statusText}
sendSingleCase={props.sendSingleCase}
/>))}
</React.Fragment>
);
}
OeTestfall
function OeTestfall(props) {
const { szenario, testid, json } = props;
const [open, setOpen] = useState(false);
function handleOpen(event) {
event.stopPropagation();
setOpen(true);
}
function handleClose() {
setOpen(false);
}
return (
<ExpansionPanel>
<ExpansionPanelSummary expandIcon={<ExpandMoreOutlined />}>
<OeTestfallSummary
szenario={szenario}
testid={testid}
json={json}
status={props.status}
handleClose={handleClose}
handleOpen={handleOpen}
open={open}
statusText={props.statusText}
errorStatus={props.errorStatus}
sendSingleCase={props.sendSingleCase}
/>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<div>ForNoError</div>
</ExpansionPanelDetails>
<ExpansionPanelActions>
<Button
variant="outlined"
color="primary"
>
Bearbeiten
</Button>
<Button
variant="outlined"
color="secondary"
>
Löschen
</Button>
</ExpansionPanelActions>
</ExpansionPanel>
);
}
OeTestfallSummery
function OeTestfallSummary(props) {
const { handleOpen } = props;
const [status, setStatus] = useState('');
const [statusText, setStatusText] = useState('');
const [errorStatus, setErrorStatus] = useState('');
useEffect(() => {
setErrorStatus(props.errorStatus);
setStatusText(props.statusText);
setStatus(props.status);
}, []);
return (
<div style={styles.summaryWrapper}>
<Typography align="center" variant="subtitle1">
TestID: {props.testid}
</Typography>
<Typography align="center" variant="subtitle1" style={{ fontWeight: 'bold' }}>
{props.szenario}
</Typography>
<Button
size="small"
variant="outlined"
color="primary"
onClick={handleOpen}
>
JSON
</Button>
<Tooltip title="VorneTooltip" style={styles.lightTooltip} placement="left">
<Chip
color="secondary"
variant="outlined"
label={status}
/>
</Tooltip>
<StatusChip
status={errorStatus}
/>
<OeJsonViewer json={JSON.parse(props.json)} open={props.open} handleClose={props.handleClose} stopEventPropagation />
<Tooltip
title="ToolTipTitel"
style={styles.lightTooltip}
placement="top"
>
<Chip
color="primary"
variant="outlined"
label={statusText}
/>
</Tooltip>
<Button variant="contained" color="primary" onClick={() => props.sendSingleCase(props.testid)} >
Run
</Button>
<Button variant="contained" color="primary" onClick={() => console.log(status)} >
test
</Button>
</div>
);
}
In my OeTestfallAccordion the prop testData does not update. If i try to console.log it inside my childComponent it has the old Value like before i execute the sendSinglecase function. What do i need to do, that i update the Data correctly that my child component gets notified that the props had changed and it has to rerender.
EDIT:
I tried some new things and can narrow down the problem. In my TestAutomationTab Component i send the whole data State to the OeTestfallAccordion Child Component. In this OeTestfallAccordion Component i split up the Array of Data which consists of multiple Objects like:
0: {id: 41, testfallid: 1, json: "{\"testCaseData\":{\"baseData\":{\"Check\":\"Thing…e\":\"alle\",\"tuwid\":\"2909\"}},\"testType\":\"Test\"}}", ID: null, businessId: null, …}
1: {id: 42, testfallid: 2, json: "{\"testCaseData\":{\"baseData\":{\"testfallid\":\"1…e\":\"alle\",\"tuwid\":\"2909\"}},\"testType\":\"Test\"}}", edcomAuftragsId: null, businessId: null, …}
When i hit the function sendSingleCase in my Parent Component TestAutomationTab i just change one single Parameter of the Object. The whole construct of Data keeps the same. The Child Component doesnt recognize that i changed something in the Object of Data.
But i dont know why? I also tried to useEffect on Props change in my Child COmponent when the props are changed. But it never gets executed even tho some attributes got updated inside the props.data.
function OeTestfallAccordion(props) {
const testData = props.testData;
const [data, setData] = useState(testData);
useEffect(() => {
setData(testData);
console.log("triggered");
}, [props]);
...
}
Okay things worked out a bit.
I changed the sendSingleCase function to first Copy the whole state in a Temp variable. Change one Attribute inside an Object and then setData (inside useState) with the tempData Variable. So the whole State gets renewed and the child components recognize the change and rerender.
But it seems not to be very fast. Always to copy the whole Data in a new Variable and then reassign it is very Ressource heavy. Is there a better solution?
function sendSingleCase(id) {
const tempState = [...data];
tempState.forEach((e) => {
if (e.testfallid === id) {
e.status = "pressed";
console.log(e.status);
}
});
setData(tempState);
}
I have a component card on the side of a page of my app that displays a user's current fitness track:
When the page loads the name of the track is blank because it hasn't been loaded from the server yet. However, I would expect that it would update the component and rerender as soon as the result was returned. Likewise, when you click the arrow of the component, a modal pops up asking if you are ready to advance to the next track. If you confirm, it calls a switchTrack() function that calls the API, and if the result is successful the user is updated and the modal closes. However, even though the track has in fact been updated, the track name doesn't refresh until you refresh the page. How do I make sure this component re-rerenders with the new text when the result comes back? (Note: You'll see I tried to force this with a reload state, but this hasn't worked).
Component Card (simplified):
const CurrentPlanCard = props => {
const { open, launchModal, closeModal } = useModal(false);
const { user } = useSelector(mapState);
const [currentTrackName, setCurrentTrackName] = useState('');
const [nextTrackName, setNextTrackName] = useState('');
const [nextTrackId, setNextTrackId] = useState('');
const [reload, setReload] = useState(false);
useEffect(() => {
if (user) {
getTrack(user.fitnessTrack)
.then(currentTrack => {
setCurrentTrackName(currentTrack.name);
setNextTrackName(currentTrack?.nextTrack.name);
setNextTrackId(currentTrack?.nextTrack._id);
})
.catch(err => {
console.error(err);
});
}
}, [user, reload, setCurrentTrackName, setNextTrackName, setNextTrackId]);
return (
<FlexContainer justify="space-between">
<LeftContainer flexDirection="column" justify="center">
<Label>Current Fitness Track:</Label>
<PlanName>{currentTrackName}</PlanName>
<Text>Ready to move to the next stage? Switch your plan now!</Text>
</LeftContainer>
<RightContainer flexDirection="column" justify="center">
<ArrowBlue onClick={launchModal} />
</RightContainer>
<SwitchPlanModal
open={open}
handleClose={closeModal}
userId={user._id}
currentTrackName={currentTrackName}
nextTrackId={nextTrackId}
nextTrackName={nextTrackName}
reload={reload}
setReload={setReload}
{...props}
/>
</FlexContainer>
);
};
Modal to switch the plan:
const SwitchPlanModal = ({
open,
handleClose,
userId,
currentTrackName,
nextTrackName,
nextTrackId,
setReload,
reload,
...props
}) => {
const [error, setError] = useState('');
const handleClick = async e => {
try {
if (nextTrackId) {
await switchTrack(userId, nextTrackId);
setReload(!reload);
setError('');
handleClose();
} else {
setError('No next track defined');
}
} catch (err) {
console.error(err);
if (err.message) {
setError(err.message);
} else if (err.error.message) {
setError(err.error.message);
}
}
};
return (
<div>
<Modal open={open} onClose={handleClose}>
<StyledDialogContent>
<Container justify="center" flexDirection="column">
<NavBar justify="space-between" alignItems="center">
<CloseRight>
<CloseIcon handleClose={handleClose} />
</CloseRight>
</NavBar>
<ModalBody
justify="space-evenly"
alignItems="center"
flexDirection="column"
>
<StyledAvatar src={`${fileStorage}/AddExercise.png`} />
<FlexContainer flexDirection="column" alignItems="center">
<Header>Ready for the next stage?</Header>
</FlexContainer>
<FlexContainer flexDirection="column" alignItems="center">
<Button
buttonText="Let's Go!"
onClick={handleClick}
/>
<Link onClick={handleClose}>Nope, not yet</Link>
{error && <ErrorMessage>{error}</ErrorMessage>}
</FlexContainer>
</ModalBody>
</Container>
</StyledDialogContent>
</Modal>
</div>
);
};
In a react-admin project I created my own toolbar button that should display a confirmation dialog, similar to the JavaScript alert but not quite as ugly.
Only when the user clicks OK, things should happen, in my case some database operations.
is there an ootb alert dialog in react-admin or what is an easy way to create one?
I could not find anything in the docs about that topic. I tried the alert example from material ui (see https://v1.material-ui.com/demos/dialogs/) but due to my very limited understandig of react I am not able to create a reusable component from the example.
Update:
The code snippet below illustrates what I'd like to do:
// Definition of a toolbar button
const ActionButton = ({ handleSubmitWithRedirect, ...props }) => {
const form = useForm();
var formdata = form.getState().values;
switch (formdata.status.id) {
case 0:
props.label = "Text for state 0";
break;
case 1:
props.label = "Text for state 2";
break;
default:
props.label = "Unknown state"
}
const handleClick = useCallback(() => {
switch (formdata.status.id) {
case 0:
form.change('status', status[1]);
break;
case 1:
// Here I want to open a confirmation Dialog...
if( openAlertDialog("Warning, things will happen","Okay","Better not"))
{
form.change('status', status[2]);
createDatabaseRecord(formdata).then(() => (
// success handling [...]
),
() => (
// error handling [...]
))
};
break;
default:
}
handleSubmitWithRedirect('list');
}, [formdata, form]);
return <SaveButton {...props} handleSubmitWithRedirect={handleClick} />;
};
There is actually a Confirm component which can be used in a toolbar button like this:
const ExampleButton = ({ handleSubmitWithRedirect, handleSubmit, ...props }) => {
const form = useForm();
const notify = useNotify();
const [open, setOpen] = React.useState(false);
const handleClick = () => setOpen(true);
const handleDialogClose = () => setOpen(false);
const handleConfirm = () => {
doStuff();
notify('Stuff is done.');
handleSubmit();
setOpen(false);
};
var ct = "Do you really want to do stuff?";
return (<><SaveButton {...props} handleSubmitWithRedirect={handleClick} handleSubmit={handleClick} variant="outlined" />
<Confirm
isOpen={open}
title="do stuff"
content={ct}
onConfirm={handleConfirm}
onClose={handleDialogClose}
confirm="Yep"
cancel="Nope"
/>
</>);
}
Check out the following codesandbox for an example on how to trigger opening a dialog using Material-UI as well as triggering different actions based on whether you click the "Agree" or "Disagree" buttons.
https://codesandbox.io/s/material-demo-cspqy
In case it interests anyone, this is the OK/Cancel Dialog I made. It was too hard to close the dialog within the component. I had to have that logic outside the component, but I couldnt find any other way of achieving the closing logic.
//Test.tsx
function Test() {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<>
<Button onClick={handleOpen}> Delete Category</Button>
{open && <OKCancelDialog open={true} title={"Delete Thing!"}
content={"Are you sure you want to delete thing?"}
handleOK={() => {
handleClose();
alert("yeah")
}}
handleCancel={() => {
handleClose();
alert("cancel")
}}/>}
</>
)
}
//OKCancelComponent.tsx
type Props = {
title: string,
content: string,
handleOK: () => any,
open: boolean
handleCancel: () => any
}
export default function OKCancelDialog(props: Props) {
return (
<Dialog
open={props.open}
onClose={props.handleCancel}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{props.title}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{props.content}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={props.handleOK}>
OK
</Button>
<Button startIcon={<CancelIcon/>} onClick={props.handleCancel}>Cancel</Button>
</DialogActions>
</Dialog>
);
}
In my case I made a new component "ButtonConfirm".
import React from 'react';
class ButtonConfirm extends React.Component
{
constructor(props)
{
super(props)
this.state = {
title: this.props.title,
classButtonName: 'buttonForm buttonAlert',
classDialogName: 'dialog_alert',
query: this.props.query,
param: "del",
param_id: "-1",
view: "button"
}
}
showDialog()
{
this.setState({
view: "query"
});
}
onClickYes()
{
this.setState({
view: "button"
});
this.props.onConfirm("yes",this.state.param, this.state.param_id);
}
onClickNo()
{
this.setState({
view: "button"
});
this.props.onConfirm("no",this.state.param, this.state.param_id);
}
render()
{
if(this.state.view == "button")
{
return (
<div className={this.state.classButtonName} onClick={this.showDialog.bind(this) }>{this.state.title}</div>
);
}
if(this.state.view == "query")
{
return (
<div className={this.state.classDialogName}>
<div>{this.state.title}</div>
<div className='container'>
<div>{this.state.query}</div>
<div className={this.state.classButtonName} onClick={this.onClickYes.bind(this) } >YES</div>
<div className={this.state.classButtonName} onClick={this.onClickNo.bind(this) } >NO</div>
</div>
</div>
);
}
}
}
export default ButtonConfirm;
Then in my "top" component I created new method
onConfirmDel(type, param, id)
{
console.log(type + param + id);
}
And in render method:
<ButtonConfirm onConfirm={this.onConfirmDel.bind(this) } title="Delete" query="Are you sure...?" />
If you want to use that, you will need css style :)