How to close semantic ui modal in another react component? - javascript

In my main component I can open a modal by clicking on an icon. The content of the modal is a separate component, which is calling a method.
If the method call is successful, I want to close the modal. But how can I do this?
Main component
class Example extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div>
<Modal trigger={ <Icon name='tags' /> } >
<Modal.Header>
<div>
<Header floated='left'>Title</Header>
<Button floated='right'>A Button</Button>
</div>
</Modal.Header>
<Modal.Content>
<ModalContent />
</Modal.Content>
</Modal>
</div>
)
}
}
Modal content
class ModalContent extends Component {
constructor(props) {
super(props)
this.state = {}
}
handleClick() {
method.call(
{ param },
(error, result) => {
if (result) {
// Now close the modal
}
}
);
}
render() {
return (
<Button onClick={this.handleClick} content='Save' />
)
}
}

You should add an onClose property to <Modal> element. See example below:
<Modal
trigger={<Button onClick={this.handleOpen}>Show Modal</Button>}
open={this.state.modalOpen}
onClose={this.handleClose}
>
Then you can add onClose function to a button in your modal. Full example from the docs:
https://react.semantic-ui.com/modules/modal#modal-example-controlled

Pass a onSuccess method as a props :
in the parent :
<ModalContent onSuccess={this.onModalSuccess}/>
in the child component :
handleClick() {
method.call(
{ param },
(error, result) => {
if (result) {
this.props.onSuccess()
}
}
);
}
In this way you keep your open/close logic in the parent component.

semantic-ui have property open. Just set true or false
class Example extends Component {
constructor(props) {
super(props)
this.state = {
open: false
}
open = () => this.setState({ open: true })
close = () => this.setState({ open: false })
render() {
return (
<div>
<Modal open={this.state.open} trigger={ <Icon name='tags' /> } >
<Modal.Header>
<div>
<Header floated='left'>Title</Header>
<Button floated='right'>A Button</Button>
</div>
</Modal.Header>
<Modal.Content>
<ModalContent />
</Modal.Content>
</Modal>
</div>
)
}
}

Related

switch icons on button click in react

I am using react-full-screen library.
Link to code sandbox
I have a navbar, where I have placed the JSX for the button with icons.
class AdminNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
isFfull: false
};
}
render() {
return (
<Navbar className="navbar" expand="lg">
<Container fluid>
<div className="navbar-wrapper">
<div className="navbar-minimize d-inline">
<button className="btn-fullscreen" onClick={this.props.goFull}>
<i className="fa fa-expand-arrows-alt"></i>
<i className="fa compress-arrows-alt"></i>
</button>
</div>
</div>
</Container>
</Navbar>
);
}
}
And then in my another Admin Component, I am using it as props and performing the onClick()
class Admin extends React.Component {
constructor(props) {
super(props);
this.state = {
isFull: false
};
}
goFull = () => {
if (document.body.classList.contains("btn-fullscreen")) {
this.setState({ isFull: true });
} else {
this.setState({ isFull: false });
}
document.body.classList.toggle("btn-fullscreen");
};
render() {
return (
<Fullscreen
enabled={this.state.isFull}
onChange={(isFull) => this.setState({ isFull })}
>
<div className="wrapper">
<div className="main-panel">
<AdminNavbar {...this.props} goFull={this.goFull} />
</div>
</div>
</Fullscreen>
);
}
}
Problem: the icons are not changing on click of the button. I also tried using the active class. but no luck.
You don't have to check the classList on body. The icon toggle can be achieved by state change.Please have a look at the code.
import React from "react";
import AdminNavbar from "./AdminNavbar";
import Fullscreen from "react-full-screen";
class Admin extends React.Component {
constructor(props) {
super(props);
this.state = {
isFull: false
};
}
goFull = () => {
this.setState({ isFull: !this.state.isFull });
};
render() {
return (
<Fullscreen
enabled={this.state.isFull}
onChange={(isFull) => this.setState({ isFull })}
>
<div className="wrapper">
<div className="main-panel">
<AdminNavbar
{...this.props}
isFull={this.state.isFull}
goFull={this.goFull}
/>
</div>
</div>
</Fullscreen>
);
}
}
export default Admin;
The admin Navbar code
import React from "react";
// reactstrap components
import { Navbar, Container } from "reactstrap";
class AdminNavbar extends React.Component {
constructor(props) {
super(props);
this.state = {
isFfull: false
};
}
render() {
return (
<Navbar className="navbar" expand="lg">
<Container fluid>
<div className="navbar-wrapper">
<div className="navbar-minimize d-inline">
<button className="btn-fullscreen" onClick={this.props.goFull}>
{!this.props.isFull ? (
<i className="fa fa-expand-arrows-alt"></i>
) : (
<i className="fa compress-arrows-alt"></i>
)}
</button>
</div>
</div>
</Container>
</Navbar>
);
}
}
export default AdminNavbar;

Reactjs: Dialog open is not working on button click

I am trying to open and close a dialog on a button click from another page/component.
But it is not working on clicking the button.
Any sugegstion what I am missing and doing wrong here with handeling modal.
Thanks in advance.
//TestComponent
class TestConnectDialog extends React.Component {
render() {
const {isOpen, onOk} = this.props;
return (
<Dialog
isopen={this.props.isopen}
onClose={this.props.handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Test
</DialogContentText>
</DialogContent>
<DialogActions className="dialog-action">
<Button onClick={this.props.handleClose} className="primary-button">
Ok
</Button>
</DialogActions>
</Dialog>
);
}
};
export default TestConnectDialog;
// Home page
import TestConnectDialog from './TestConnectDialog';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
this.handleTestConnectClick = this.handleTestConnectClick.bind(this);
//this.handleCloseDialog = this.handleCloseDialog.bind(this);
}
handleTestConnectClick= () =>{
this.setState({ isOpen: true });
}
render() {
const {isOpen, onOk} = this.props;
return (
<div className="section">
<Button className="connect-test-button"
onClick={this.handleTestConnectClick}>
Test
</Button>
<TestConnectDialog isOpen={this.state.isOpen} />
</div>
);
}
};
export default HomePage;
Your prop name is spelled incorrectly, it should be this.props.isOpen also a quick little tip, it is possible to use just one function for opening/closing the modal.
Something like this will work:
handleTestConnectClick = () => {
this.setState(prevState => ({
...prevState,
isOpen: !prevState.isOpen
}));
}
here we use our previous state and with the ! operator we switch from true to false and vice versa
Update 2.0:
After taking a closer look at the Material UI documentation, I noticed that your dialog prop for setting the modal visibility is wrong. It should be open instead of isOpen.
import TestConnectDialog from './TestConnectDialog';
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
//this.handleTestConnectClick = this.handleTestConnectClick.bind(this);
//this.handleCloseDialog = this.handleCloseDialog.bind(this);
// when using arrow functions you don't need to bind the this keyword
}
handleTestConnectClick = () => {
this.setState(prevState => ({
...prevState,
isOpen: !prevState.isOpen
}));
}
render() {
return (
<div className="section">
<Button className="connect-test-button"
// onClick={this.handleTestConnectClick}>
// change the onClick to the one below
onClick={ () => this.handleTestConnectClick() }
Test
</Button>
<TestConnectDialog isOpen={this.state.isOpen} handleTestConnectClick={this.handleTestConnectClick}/>
</div>
);
}
};
export default HomePage;
In TestConnectDialog component:
class TestConnectDialog extends React.Component {
render() {
return (
<Dialog
open={this.props.isOpen}
onClose={this.props.handleTestConnectClick}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Test
</DialogContentText>
</DialogContent>
<DialogActions className="dialog-action">
<Button onClick={this.props.handleTestConnectClick} className="primary-button">
Ok
</Button>
</DialogActions>
</Dialog>
);
}
};
export default TestConnectDialog;
You're passing the props <TestConnectDialog isOpen={this.state.isOpen} /> but trying to read it with isopen={this.props.isopen}.
Change your code to this: isopen={this.props.isOpen}
Update TestComponent component as given
class TestConnectDialog extends React.Component {
render() {
const {isOpen, onOk} = this.props;
return (
<Dialog
isopen={isOpen}
onClose={this.props.handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Test
</DialogContentText>
</DialogContent>
<DialogActions className="dialog-action">
<Button onClick={this.props.handleClose} className="primary-button">
Ok
</Button>
</DialogActions>
</Dialog>
);
}
};
export default TestConnectDialog;
In the homepage component why is isOpen destructured from the prop and initialised in state. You have to use one, using both is confusing, you are working with that on the state but passing the one from the prop

Isolating a function when data is mapped in react

I have data being mapped as a repeater. But I need to isolate the opening function (It's an accordion). I'm still learning my way through React. Basically, the accordions load with the state for open: false Once the ListItem is clicked, the HandleClick function toggles the state to open: true. A simple concept, I just need to isolate it so that it works independently. Whereas right now they all open and close at the same time.
Here is the state in a constructor and function
constructor(props) {
super(props);
this.state = {
open: true,
};
}
handleClick = () => { this.setState({ open: !this.state.open }); };
Here is my mapping script in ReactJS
{LicenseItems.map((item, index) => (
<div key={index}>
<ListItem
divider
button
onClick={this.handleClick}>
<ListItemText primary={<CMLabel>{item.accordion_name}</CMLabel>}/>
</ListItem>
<Collapse
in={!this.state.open}
timeout="auto"
unmountOnExit>
{item.content}
</Collapse>
</div>
))}
The in dictates whether it is open or not per MaterialUI-Next
Thanks in advance guys!
Not very pretty, but something like this should work:
constructor(props) {
super(props);
this.state = {
open: {},
};
}
handleClick = (idx) => {
this.setState(state => ({open: { [idx]: !state.open[idx]} }))
}
// in render
{LicenseItems.map((item, index) => (
<div key={index}>
<ListItem
divider
button
onClick={() => this.handleClick(index)}>
<ListItemText primary={<CMLabel>{item.accordion_name}</CMLabel>}/>
</ListItem>
<Collapse
in={!this.state.open[index]}
timeout="auto"
unmountOnExit>
{item.content}
</Collapse>
</div>
))}
It would be better to create separate Components for that, which have their own open state.
You should create two components for that:
Accordions.js
import React from 'react'
import Accordion from './Accordion'
const Accordions = props => {
return (
props.LicenseItems.map((item, index) => (
<Accordion key={index} item={item} />
))
);
}
export default Accordions;
Accordion.js
import React, { Component } from 'react'
class Accordion extends Component {
constructor(props) {
super(props);
this.state = {
open: true,
};
}
handleClick = () => { this.setState({ open: !this.state.open }); };
render() {
return (
<div>
<ListItem
divider
button
onClick={this.handleClick}>
<ListItemText primary={<CMLabel>{this.props.item.accordion_name}</CMLabel>}/>
</ListItem>
<Collapse
in={!this.state.open}
timeout="auto"
unmountOnExit>
{this.props.item.content}
</Collapse>
</div>
)
}
}
export default Accordion;

Updating child property from parent in React Native

I want to be able to update a child component property from both the parent and itself according to the last event.
For example:
class MyParent extends Component {
state ={
text:"";
}
render() {
return (
<View>
<MyChild text={this.state.text} />
<Button
onPress={()=>this.setState({text:"parent"})}
title="Update From Parent"
/>
</View>
);
}
}
class MyChild extends Component {
state ={
text:"";
}
componentWillReceiveProps(nextProps) {
if (nextProps.text!== this.state.text) {
this.setState({text:nextProps.text});
}
}
render() {
return (
<View>
{/* I want that the text field will be updated from the last event*/}
<Text>{this.state.text}</Text>
<Button
onPress={()=>this.setState({text:"child"})}
title="Update From Child"
/>
</View>
);
}
}
The issue is that componentWillReceiveProps is triggered each time the setState is called so the text property takes the value from the parent and not from the child.
How can I achive this result?
Thanks a lot
Elad
Manage your state through parent component and pass the function that will update the state of parent component in child component
class MyParent extends Component {
constructor(props) {
super(props);
this.state = {
text: "",
updateParentState: (newState) => this.setState(newState)
}
}
render() {
let { text, updateParentState } = this.state;
return (
<View>
<MyChild data={{ text, updateParentState }} />
<Button
onPress={() => updateParentState({ text: "parent" })}
title="Update From Parent"
/>
</View>
);
}
}
class MyChild extends Component {
constructor(props) {
super(props);
this.state = {
text: props.data.text
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.text !== this.state.text) {
this.setState({ text: nextProps.data.text });
}
}
render() {
let { updateParentState } = this.props.data;
return (
<View>
<Text>{this.state.text}</Text>
<Button
onPress={() => updateParentState({ text: "child" })}
title="Update From Child"
/>
</View>
);
}
}
You are missing this. in the setState call in child. please check if this is not the issue.
<Button
onPress={()=>setState({text:"child"})}
title="Update From Child"
/>
should be
<Button
onPress={()=>this.setState({text:"child"})}
title="Update From Child"
/>

Resetting state on component in React

I have a simple problem in React JS. I have two different click events, which switch the state of the component. The first one works perfectly, however I cannot get the second event to reset the component back to its original state. This is a stripped down version of my problem, so just know that I cannot move the click functions into the Child component.
class Parent extends Component{
constructor(){
this.state = {
open: false
}
this.handleOpen = this.handleOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
}
handleOpen(){
this.setState({open: true})
}
handleClose(){
this.setState({open: false})
}
render(){
return(
<div>
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} />
</div>
)
}
}
Like I said, the handleOpen function switches the state, but the handleClose does not switch it back. I can get a console log to show on the handleClose function, so I know that it does not have to do with how it is being hooked up to the Child Component. Am I missing something about how to reset a state value after it has already been switched. Thank you for your help!
Here is How you have to do it!
class Child extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.props.isOpen);
if (this.props.isOpen) {
this.props.onClose();
} else {
this.props.onOpen();
}
}
render() {
return <button onClick={this.handleClick}>Click ME</button>;
}
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {
open: false
}
this.handleOpen = this.handleOpen.bind(this)
this.handleClose = this.handleClose.bind(this)
}
handleOpen(){
this.setState({open: true})
}
handleClose(){
this.setState({open: false})
}
render(){
return(
<div>
<p>{this.state.open.toString()}</p>
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
<Child onOpen={this.handleOpen} onClose={this.handleClose} isOpen={this.state.open} />
</div>
)
}
}
ReactDOM.render(
<Parent/>,
document.getElementById('container')
);

Categories