I am using react for the project. Here is my code for parent component:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
deleteConfirm: false
};
}
onDelete = pass => {
this.setState({ deleteConfirm: false });
if (pass === true) {
const { menu, onDelete, restaurantId } = this.props;
//onDelete(menu.id, restaurantId);
}
};
openDiag() {
this.setState({ deleteConfirm: true });
}
render() {
return (
<div>
<Button
className={classes.button}
variant="contained"
color="secondary"
onClick={this.openDiag.bind(this)}
>
Delete
{this.state.deleteConfirm ? (
<Child onDelete={this.onDelete.bind(this)} />
) : null}
<Delete className={classes.rightIcon} />
</Button>
</div>
);
}
}
The parent component has state called deleteConfirm, If I press Delete button, deleteConfirm is set to true and the child component is rendered. The function OnDelete passes to child component,
the child component is a Material Ui Dialog which is used to confirm the delete action. Here is the code:
class Child extends Component {
state = {
open: true
};
handleClose = (e, confirm) => {
const { onDelete } = this.props;
if (confirm === true) {
onDelete(true);
} else {
onDelete(false);
}
this.setState({ open: false });
};
render() {
return (
<div>
<Dialog
open={this.state.open}
onClose={e => this.handleClose(e, false)}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogActions>
<Button onClick={e => this.handleClose(e, false)}
color="primary">
Cancel
</Button>
<Button
onClick={e => this.handleClose(e, true)}
color="primary"
autoFocus
>
Confirm
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
If I press the cancel button, the delete action is cancelled and the parent function onDelete is called. the deleteConfirm in parent component should be set to false. However, the state is always true and I can't change it. I am greatly appreciated for anybody who would like to help me. Thanks!
Classic Event Propagation mistake. You embedded a click handler inside another click handler which reversed the logic of the inner one.
Use event.stopPropagation in your inner handler or rearrange your elements such that they are not nested within one another:
render() {
return (
<div>
<Button
className={classes.button}
variant="contained"
color="secondary"
onClick={this.openDiag.bind(this)}
>
Delete
<Delete className={classes.rightIcon} />
</Button>
{this.state.deleteConfirm ? (
<Child onDelete={this.onDelete.bind(this)} />
) : null}
</div>
);
Related
I'm working on A React project with GraphQl back-end. I have a modal where a user can view more details about a subject. In that modal you can click delete which opens a new modal where you need to confirm if you wan't to delete. When yes is pressed it should be deleted and the modal should close. When no is pressed the modal should just close. The deleting of the subject works. If I press yes is deletes it but the modal doesn't close and when I press no the modal also doesn't close. Can anyone explain why and how I can fix this?
parent modal:
class CalenderModal extends React.Component {
constructor(props) {
super(props);
this.state = {
openDeleteAppointment: false,
};
this.openDeleteAppointment = this.openDeleteAppointment.bind(this);
}
handleRemove = () => {
this.props.onRemove();
}
openDeleteAppointment() {
this.setState({
openDeleteAppointment: true,
})
}
render() {
return (
<React.Fragment>
<div className="customModal">
<div className="modal-header">
<h5 className="customModal__text"> Appointment summary</h5>
<button className="btn modal__button__red" onClick={() => { this.openDeleteAppointment() }}>Delete</button>
{this.state.openDeleteAppointment &&
<DeleteAppointmentModal appointment={this.state.id} onHide={() => this.setState({ openDeleteClient: false, id: null })} show />}
</div>
<div className="modal-container">
<div className="summary">
<button className="btn modal__button__cancel" onClick={this.handleRemove}>Cancel</button>
</div>
</div>
}
</React.Fragment>
);
}
export default CalenderModal;
child modal:
class DeleteAppointmentModal extends React.Component {
constructor(props) {
super(props);
this.state = {
id: this.props.appointment,
};
}
render() {
const {id} = this.state
const DELETE_MUTATION = gql`
mutation DeleteMutation($id:ID! ) {
deleteAppointment(id:$id) {
id
}
}
`
console.log("delete id",this.state.id)
return (
<React.Fragment>
{
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-update-client"
centered
>
<Modal.Header closeButton >
<Modal.Title id="contained-modal-title-vcenter" className="tittle">Delete appointment </Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="delete-content">
Are you sure you want to delete this appointment?
</div>
</Modal.Body>
<Modal.Footer>
<button onClick={() => this.props.onHide() } className="btn no">No</button>
<Mutation mutation={DELETE_MUTATION}
variables={{id}}>
{/* onCompleted={() => this.props.history.push('/')} */}
{deleteMutation =>
<button onClick={() => { deleteMutation(); this.props.onHide() }} className="btn yes">Yes</button>
}
</Mutation>
</Modal.Footer>
</Modal>
}
</React.Fragment>
);
}
}
export default DeleteAppointmentModal;
On "return" of parent component change the following row:
from:
<DeleteAppointmentModal appointment={this.state.id} onHide={() => this.setState({ openDeleteClient: false, id: null })} show />}
to:
<DeleteAppointmentModal appointment={this.state.id} onHide={() => { this.setState({ openDeleteAppointment: false, id: null }); handleRemove(); }} show />}
Hope it solved the problem.
From observation:
the show prop is always true. set show={this.state. openDeleteAppointment}
the onHide is not setting the right state. it should set openDeleteAppointment instead of openDeleteClient
I am opening Model (child Component) on Button Click from Parent Component, it opens very well but its not closing and it shows some error:
Uncaught TypeError: setOpen is not a function from Child Component
Here is My Parent Component
<TableCell>
<Button
variant="contained"
size="small"
color="primary"
onClick={() => deleteHandler(index)}
>
Delete Me
</Button>
</TableCell>
{console.log(open)}
{open && <AddList open={open} setOpen={open} />}
My Child Component
export default function TransitionsModal(open, setOpen) {
const classes = useStyles();
// const [openL, setOpenL] = React.useState(null);
// const handleOpen = () => {
// setOpen(true);
// };
const handleClose = () => {
setOpen(!open);
};
return (
<div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">Transition modal</h2>
<p id="transition-modal-description">
react-transition-group animates me.
</p>
</div>
</Fade>
</Modal>
</div>
);
}
Your first issue is that you are passing a Boolean for the setOpen prop rather than the setOpen function itself, so change it to setOpen={setOpen}.
// RenderList.js
const RenderList = props => {
// ...
return (
...
{open && <AddList open={open} setOpen={setOpen} />}
)
}
Your second issue is that you're not destructing props properly in the TransitionsModal component. Use {} to destruct the props object and grab what you need.
// AddList.js
export default function TransitionsModal({ open, setOpen }) {
// ...
}
Here's the fixed example:
CodeSandbox
Hope this helps.
Hi take a look at this
https://codesandbox.io/s/frosty-bird-5yh5g
in RenderList.js you didn't pass setOpen
{open && <AddList open={open} setOpen={setOpen} />}
also export default function TransitionsModal({ open, setOpen }) {
I want to control of 'Opening and Closing Modals' without using Redux. If I use Redux, i create a variable as isVisible and control it with setting a value as true or false. But, If I dont use Redux, i cant solve problem that explained below.
It's the code that I call modal function.
<TouchableOpacity onPress={ () => this.setState({ visibleModal: true ])}>
<Text> Press me! </Text> </TouchableOpacity>
<ProfileModals isVisible={this.state.visibleModal} />
Main Modal function:
export default class ProfileModals extends Component {
state = {
visibleModal: this.props.isVisible,
modal: "changeName"
};
render() {
const {visibleModal} = this.state;
return (
<Modal
isVisible={this.state.visibleModal}
backdropColor="black"
backdropOpacity={0.5}
animationOut="fadeOut"
onBackdropPress={() => this.setState({ visibleModal: false })}
swipeDirection={"down"}
onSwipeComplete={() => this.setState({ visibleModal: false })}
>
<View>
<Text>Hello!</Text>
</View>
</Modal>
);
}
}
When I press the button at the first time, Modal run correctly. After closing the modal, I press the button second time, It doesn't run anymore. Because of this.state.visibleModal value (in ProfileModal Component) is false, not this.props.isVisible.
How can i solve this problem?
dont store the modal's visibility in the state of the modal component, allow it to flow through props from the parent, follow this approach:
class Parent extends Component {
state = {
modalVisible: false,
};
openModal = () => this.setState({ modalVisible: true });
closeModal = () => this.setState({ modalVisible: false });
render() {
const { modalVisible } = this.state;
return (
<OtherStuff>
<Button onPress={this.openModal} />
<MyModal
visible={modalVisible}
closeModal={this.closeModal}
/>
</OtherStuff>
);
}
}
class MyModal extends Component {
render() {
const { visible, closeModal } = this.props;
return (
<Modal isVisible={visible}>
<Button onPress={closeModal} />
</Modal>
);
}
}
First I define this component to reuse with modal windows
Template 1:
...
import Modal from 'react-native-modal';
class CustomModal extends Component {
constructor(props){
super(props);
}
componentWillReceiveProps(nextProps){
this.setState({
visible: nextProps.isVisible
})
}
state = {
visible: false
}
render() {
return (
<TouchableWithoutFeedback
onPress={()=>{
this.setState({
visible: false
});
}}
>
<Modal
isVisible={this.state.visible}
>
<TouchableWithoutFeedback onPress={() => {}}>
<View>
{this.props.content}
</View>
</TouchableWithoutFeedback>
</Modal>
</TouchableWithoutFeedback>
);
}
}
Now in a second component I call it:
import Modal from './common/modal';
return (<Modal
isVisible={this.state.showModal}
content={this.renderMyContent()}
/>
)
Clicking in a button I setState showModal : true, my modal is open, I can click outside the modal and actually the modal disappear but my state.showModal still being : true, how can I update the state in this view?
You can pass through a callback to the onModalHide prop of react-native-modal.
In your CustomModal:
<Modal onModalHide={this.props.onModalHide} ... />
In your "second template"
<Modal onModalHide={() => this.setState({ showModal: false })} ... />.
Note also that there's no need to track visibility within your CustomModal's state, since it already has its visibility in a prop. Just pass the prop down to the inner Modal directly: <Modal isVisible={this.props.isVisible} .../>
Call this.setState({showModal: false}) also in React we refer to these as components not "templates"
Create a function hideModal in the class where you are calling <Modal /> like this:
hideModal = () => {
this.setState({showModal: false})
}
and pass it to the <Modal /> as a prop follows:
import Modal from './common/modal';
return (<Modal
isVisible={this.state.showModal}
content={this.renderMyContent()}
hideModal={this.hideModal}
/>
)
And for closing call the hideModal prop as passed above from your CustomModal component as follows:
...
import Modal from 'react-native-modal';
class CustomModal extends Component {
constructor(props){
super(props);
}
componentWillReceiveProps(nextProps){
this.setState({
visible: nextProps.isVisible
})
}
state = {
visible: false
}
render() {
return (
<TouchableWithoutFeedback
onPress={()=>{
this.props.hideModal()
}}
>
<Modal
isVisible={this.state.visible}
>
<TouchableWithoutFeedback onPress={() => {}}>
<View>
{this.props.content}
</View>
</TouchableWithoutFeedback>
</Modal>
</TouchableWithoutFeedback>
);
}
}
I am using the IconButton from material-ui and I want to change the icon of the button after the click/touch event.
var tableModeElement =
<IconButton key="tableModeButton"
onTouchTap={() => {
this.setState(prevState => (
{ isCardView: !prevState.isCardView })) } }>
<i className="material-icons theme-color-p1">
{this.state.isCardView ? "view_module" : "list"}
</i>
</IconButton>
The expression {this.state.isCardView ? "view_module" : "list"} is only evaluated once and no more after that. I thought if I change the state I will force a re-render? What am I doing wrong?
EDIT: added that the iconButton is assigned to a variable. If I include the <IconButton> directly into the render method it works fine. I had to re-assign the variable to make it work.
I believe this would work:
class IconButton extends React.Component {
constructor(props) {
super(props);
this.state = {
isCardView: false,
}
}
render() {
return (
<a className="btn btn-primary" onClick={()=>this.setState({ isCardView: !this.state.isCardView })}>
{ this.state.isCardView
? <span className="glyphicon glyphicon-remove-sign" />
: <span className="glyphicon glyphicon-ok-sign" />
}
BUTTON
</a>
);
}
}
class App extends React.Component {
render () {
return (
<div>
<IconButton />
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
I'm using bootstrap, but any icon system would work
http://codepen.io/cjke/pen/MJvXNo?editors=0010
Setting text "view_module" or "list" in a <i> element will not change the icon for the button
You need to have a nested icon within the button, eg:
<IconButton key="tableModeButton"
onTouchTap={() => {
this.setState({
isCardView: !this.state.isCardView
})
}}>
{this.state.isCardView ? <IconA /> : <IconB /> }
</IconButton>
This is the best I could come up with :
<IconButton key="tableModeButton" onTouchTap={(e) => {
let show = this.state.prevState.isCardView;
let index = show.indexOf('show');
if (index != -1) {
show = 'hide';
} else {
show = 'show';
}
this.setState(prevState => ({ isCardView: show }));
event.preventDefault()
} }>
The icon to be used by <IconButton> is defined by it's iconClassName prop.
It could look something like this afterwards.
<IconButton key="tableModeButton"
onTouchTap={() => {
this.setState( prevState => {isCardView: !prevState.isCardView} ) }
}
iconClassName={this.state.isCardView ? "view_module" : "list"} />
</IconButton>