Clicking a button to open dialog in ReactJS - reactjs

I'm working with React MDL library, and I used pre-defined components like FABButton
<FABButton>
<Icon name="add"/>
</FABButton>
And it shows the button as in the image bellow:
Now, what I want is showing a dialog with the + icon... not as what happens here:
This happened after this code:
<FABButton>
<AddingProject />
<Icon name="add" />
</FABButton>
The class of dialog is as follows:
class AddingProject extends Component {
constructor(props) {
super(props);
this.state = {};
this.handleOpenDialog = this.handleOpenDialog.bind(this);
this.handleCloseDialog = this.handleCloseDialog.bind(this);
}
handleOpenDialog() {
this.setState({
openDialog: true
});
}
handleCloseDialog() {
this.setState({
openDialog: false
});
}
render() {
return (
<div>
<Button colored onClick={this.handleOpenDialog} raised ripple>
Show Dialog
</Button>
<Dialog open={this.state.openDialog} onCancel={this.handleCloseDialog}>
<DialogTitle>Allow data collection?</DialogTitle>
<DialogContent>
<p>
Allowing us to collect data will let us get you the information
you want faster.
</p>
</DialogContent>
<DialogActions>
<Button type="button">Agree</Button>
<Button type="button" onClick={this.handleCloseDialog}>
Disagree
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
export default AddingProject;
The above code is with the required import statements

This works with me....
First step: I added the component of the modal as follows:
<FABButton>
<Icon name="add" />
</FABButton>
<ProjectModal>
Second step: I added this prop: visible for the component as here:
<ProjectModal visible={this.state.showDialog} />
And here you need to add showDialog to the states in your class with false.
state = {
showDialog: false
};
Now, to step 3.
Third step: Add this part to your code, to be called when you click.
openModal = () => {
this.setState({ showDialog: true });
};
On the other side, you need to implement onClick in the button as follows:
<FABButton onClick={this.openModal.bind(this)}>
<Icon name="add" />
</FABButton>
Fourth step: In the modal/dialog class, you need to store the visible in a new state variable, which is here showDialogModal
constructor(props, context) {
super(props, context);
this.state = {
showDialogModal: this.props.visible
};
}
Now, you need to pass the changed state from the first class to the modal/dialog class, there are more than one option that React gives you, I used this one in fifth step. Fifth step: use this React event componentWillReceiveProps as below.
componentWillReceiveProps(nextProps) {
if (this.props.showDialogModal != nextProps.visible) {
this.setState({
showDialogModal: nextProps.visible
});
}
}
This will reflect any change in visible property from the first class to our new one here which is showDialogModal
Now in the render part, you need to check the docs of your components, here I started with React-Bootstrap.
Sixth step: use the show property in your component.
<Modal show={this.state.showDialogModal} onHide={this.closeModal}>
onHide is for closing the dialog, which makes you need to implement this too.
closeModal = () => {
this.setState({ showDialogModal: false });
};
Finally, in the closing button, add this:
<Button onClick={this.closeModal.bind(this)}>Close</Button>
Good luck.

Related

react-bootstrap modal not reacting to property change

I am using React v18.1, react-bootstrap v2.4. I have a Modal component I am trying to get to display upon a button press. The modal component is quite simple:
class AdjustmentModal extends React.Component {
constructor(props) {
super(props);
this.state = {
'show': this.props.show
};
this.handleClose = this.handleClose.bind(this);
}
handleClose() {
this.setState({ show: false })
}
render() {
return (
<Modal show={this.state.show} onHide={this.handleClose}>
[ ... Modal Content Here ... ]
</Modal>
);
}
}
export default AdjustmentModal;
As you can see, I bind the modal's show property to the value of show in state.
Then, in the component in which I want to display my modal, I have the following:
// Within render() ...
<AdjustmentModal
show={this.state.showAdjustment}
partNo={this.state.partNo}
onHandQty={this.state.onHandQty}
/>
// Futher on in the code, display the modal on click:
<Button className="icon" onClick={this.handleDisplayAdjustment}>
<i className="bi bi-pencil-square"></i>
</Button>
handleDisplayAdjustment :
handleDisplayAdjustment(event) {
event.preventDefault();
this.setState({
showAdjustment : true
});
}
Now, despite the value showAdjustment in the parent component changing to true, the modal doesn't display.
I could set the <Modal show={this.props.show} .../> instead, but props are read-only, so there is no way to close the modal again if reading from props rather than state.
You can use props, which is a better way to handle this if you want to close it then pass a method from the parent which when called update the state in the parent to false and due state update the parent component will re render and though the child component that is the modal component and the Modal will get the updated value which will be false. below is the code on how you can achieve that.
closeModal() {
this.setState({
showAdjustment: false
})
}
// Within render() ...
<AdjustmentModal
show={this.state.showAdjustment}
partNo={this.state.partNo}
onHandQty={this.state.onHandQty}
onClose={this.closeModal.bind(this)}
/>
// Futher on in the code, display the modal on click:
<Button className="icon" onClick={this.handleDisplayAdjustment}>
<i className="bi bi-pencil-square"></i>
</Button>
For the child component
class AdjustmentModal extends React.Component {
handleClose() {
this.props.onClose()
}
render() {
return (
<Modal show={this.props.show} onHide={this.handleClose}>
[ ... Modal Content Here ... ]
</Modal>
);
}
}
export default AdjustmentModal;
EDIT: Explaining the approach
This will make your Modal component a Controlled component that is controlled by Parent, also updating props as a state inside the child component is not the right way, which may create potential bugs.

Rewriting DevExtreme Dialog / Popup component to MaterialUI equivalent (react / javascript)

I'm moving away from using DevExtreme/DevExpress components, and the last one to go is my Popup / Dialog component.
The way the old popup worked, is that it was rendered in the background. The popup is rendered, but hidden, until a visible attribute is passed to it. I had it coded as the following:
<UniversalPopupComponent
popupDefinition={ownerDatabaseView}
popupValues={databaseRowData}
onSubmit={this.myPopupSubmitted}
onCancel={this.myPopupCancelled}
visible={this.state.popupVisibility}
/>
Material UI component does not seem to work the same way. For starters, how do I tell MaterialUI Dialog to only show when I click a button where the Dialog component is called, before I get to the part of actually adding data and processing data that is passed inside to generate the content?
I currently have the MUI progress (PopupMUI.js) :
class UniversalPopupComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
open: this.props.open
}
}
handleClickClose = () => {
this.setState({open: false})
}
render() {
return (
<Dialog open={this.state.open} onClose={this.handleClickClose}>
<DialogContent>
placeholder
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickClose}> Close </Button>
<Button onClick={this.handleClickClose}> Submit </Button>
</DialogActions>
</Dialog>
)
}
}
export default UniversalPopupComponent;
and in App.js contains the following related things:
class App extends React.Component {
state = {
open: false,
};
handlePopupOpen = () => {
this.setState({open:true})
}
render() {
return (
<div> ...
<Button onClick={this.handlePopupOpen}> Open Menu </Button>
<UniversalPopupComponent open={this.state.open} />
... </div>
)}
I cannot get the Dialog to open this way, by passing the state of 'open' as 'true' to the Dialog inside the component with props.
However, if I have the below, then Dialog opens fine and closes fine too (click close is defined as just setState({open:false})
<Button onClick={this.handlePopupOpen}> Open Menu </Button>
<Dialog open={this.state.open} onClose={this.handleClickClose}>
<DialogContent>
placeholder
</DialogContent>
<DialogActions>
<Button onClick={this.handleClickClose}> Close </Button>
<Button onClick={this.handleClickClose}> Submit </Button>
</DialogActions>
</Dialog>
I have not set up to pass any data yet, as the big problem is that the dialog won't open for me to start setting everything up and re-writing parts.
So my stupid brain completely forgot that, once something is in constructor, it does not get updated. So I was passing the open prop, but only writing it once.
I simply added a componentDidUpdate that compares current state of Open to new state of Open from prop, if they are different, write new state of prop.
Then I added a callback, so that when something is closed, it sets the state of open to false, which updates the component and closes the menu.

How to pass a callback to a child without triggering it

I have a React app with modal, that pop-ups with rules of the game when one clicks a button. What I want to do is make it so when I click anywhere outside this pop up window it will close. i have three files. app.js, dialog.js, and outsidealerter.js . In my main app.js when I click a button it sets a state to visible, so my element takes it and renders based upon it. my outsideralerer.js basicly detects if there is a click outside anything wrapped with specific tags. Now the problem comes that i have a method that changes the state of visibility in app.js, so in order for outsderalerter.js to use it, I pass it to it so it can have access to my main state and change it so that when a click is outside the zone the pop up window disappears. Kind of works except it closes it down even if i click within a pop up window, because when i pass the value to outsidealerter it considers the whole body as a no click zone. My question is how can I prevent it from triggering and just pass it a value, or is it possible to change the state value of app.js from outsidealerter.js
App.js
updateState() {
this.setState({ isOpen: false });
}
<div id='rule-button'>
<button onClick={(e)=>this.setState({isOpen : true})} id="modalBtn" class="button">Open Rules</button>
</div>
<OutsideAlerter updateParent={ this.updateState.bind(this)}/>
<Dialog isOpen={this.state.isOpen} onClose={(e)=>this.setState({isOpen : false})}>
</Dialog>
outsidealerter.js
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
//alert('You clicked outside of me!');
{this.props.updateParent()};
}
}
I think it will be simpler to have the modal take the full space of the window height and width and just make it invisible except for the content of what you want to show.
We can wrap the modal with onClick={hideModal} and wrap the inner content with onClick={e => e.stopPropagation()} which will prevent our wrapper for triggering the hideModal handler.
class ModalWrapper extends React.Component {
state = { isModalOpen: true };
toggleModal = () => {
this.setState(({ isModalOpen }) => ({
isModalOpen: !isModalOpen
}));
};
render() {
const { isModalOpen } = this.state;
return (
<div className="App">
<button onClick={this.toggleModal}>Open Modal</button>
{isModalOpen && <Modal hideModal={this.toggleModal} />}
</div>
);
}
}
function Modal({ hideModal }) {
return (
<div onClick={hideModal} className="modal">
<div onClick={e => e.stopPropagation()} className="modal__content">
Modal content
</div>
</div>
);
}
Working example

Is it okay to call setState on a child component in React?

I have some text. When you click on that element a modal pops up that lets you edit that text. The easiest way to make this work is to call setState on the child to initialise the text.
The other way, although more awkward, is to create an initial text property and make the child set it's text based on this.
Is there anything wrong with directly calling setState on the child or should I use the second method?
Although it is recommended to keep the data of your react application "up" in the react dom (see more here https://reactjs.org/docs/lifting-state-up.html), I don't see anything wrong with the first aproach you mentioned.
If you have to store data that is very specific of a child I don't see anything wrong in keep that information in the child's state.
It seems that your modal doesn't need to have its own state, in which case you should use a stateless React component.
This is one way of passing the data around your app in the React way.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
initialText: "hello",
}
this.saveChildState = this.saveChildState.bind(this);
}
saveChildState(input) {
console.log(input);
// handle the input returned from child
}
render() {
return (
<div>
<ChildComponent
initialText={this.state.initialText}
save={this.saveChildState}
/>
</div>
);
}
}
function ChildComponent(props) {
return (
<div>
<input id="textInput" type="text" defaultValue={props.initialText}>
</input>
<button onClick={() => props.save(document.getElementById('textInput').value)}>
Save
</button>
</div>
)
}
Maybe I am misinterpreting your question, but I think it would make the most sense to keep the modal text always ready in your state. When you decide to show your modal, the text can just be passed into the modal.
class Test extends Component {
constructor() {
this.state = {
modalText: 'default text',
showModal: false
}
}
//Include some method to change the modal text
showModal() {
this.setState({showModal: true})
}
render(
return (
<div>
<button onClick={() => this.showModal()}>
Show Modal
</button>
{ this.state.showModal ? <Modal text={this.state.modalText}/> : null }
</div>
)
)
}

Can't get button component value onClick

I'm sure this is something trivial but I can't seem to figure out how to access the value of my button when the user clicks the button. When the page loads my list of buttons renders correctly with the unique values. When I click one of the buttons the function fires, however, the value returns undefined. Can someone show me what I'm doing wrong here?
Path: TestPage.jsx
import MyList from '../../components/MyList';
export default class TestPage extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.handleButtonClick = this.handleButtonClick.bind(this);
}
handleButtonClick(event) {
event.preventDefault();
console.log("button click", event.target.value);
}
render() {
return (
<div>
{this.props.lists.map((list) => (
<div key={list._id}>
<MyList
listCollection={list}
handleButtonClick={this.handleButtonClick}
/>
</div>
))}
</div>
);
}
}
Path: MyListComponent
const MyList = (props) => (
<div>
<Button onClick={props.handleButtonClick} value={props.listCollection._id}>{props.listCollection.title}</Button>
</div>
);
event.target.value is for getting values of HTML elements (like the content of an input box), not getting a React component's props. If would be easier if you just passed that value straight in:
handleButtonClick(value) {
console.log(value);
}
<Button onClick={() => props.handleButtonClick(props.listCollection._id)}>
{props.listCollection.title}
</Button>
It seems that you are not using the default button but instead some sort of customized component from another libray named Button.. if its a customezied component it wont work the same as the internatls might contain a button to render but when you are referencing the event you are doing it throug the Button component

Resources