Get modal to fire from parent component - reactjs

Noob question here. How do I fire the toggle component of the Modal from the parent component? I want the modal to open when the button is clicked on the parent component. The button will submit a form and once the form has been submitted I want the modal to fire. I'm trying to get it to fire in function handleSubmit(e)
Child Component: Modal.jsx
class ModalExample extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState(prevState => ({
modal: !prevState.modal
}));
}
render() {
return (
<div>
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<ModalHeader toggle={this.toggle}>Modal title</ModalHeader>
</Modal>
</div>
);
}
}
Path: Parent.jsx
export default function ForgotPassword() {
function handleSubmit(e) {
e.preventDefault();
}
return (
<div className="container h-100">
<ModalExample toggle />
<button onClick={this.handleSubmit}>Open modal</button>
</div>
);
}

You can use refs as follows
export default function ForgotPassword() {
function handleSubmit(e) {
e.preventDefault();
}
return (
<div className="container h-100">
<ModalExample ref={instance => { this.child = instance; }} />
<button onClick={() => { this.child.toggle(); }}>Open modal</button>
</div>
);
}

There might be better ways, but the way I've done it is by pulling the toggle function to the parent class and passing both the function and the state value to the modal in props.
You can then call that props function in your modal to toggle its display, although this will turn your pure component into a stateful component.
Child Component: Modal.jsx
class ModalExample extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Modal isOpen={this.props.modal} toggle={this.props.toggle} className={this.props.className}>
<ModalHeader toggle={this.props.toggle}>Modal title</ModalHeader>
</Modal>
</div>
);
}
}
Parent: Parent.jsx
class ForgotPassword() extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState(prevState => ({
modal: !prevState.modal
}));
}
return (
<div className="container h-100">
<ModalExample toggle={this.toggle} modal={this.state.modal} />
<button onClick={this.handleSubmit}>Open modal</button>
</div>
);
}

Related

Change the state of parent component on click of a child component with a specific key in React JS

I am making a Todo App in React JS, i'm fairly new to React JS.
I have made two components named AddComponent and TodoItem component. The AddComponent has an input and button to add the todos and it will render TodoItem as a child component whenever a todo is added using the AddComponent.
I also have to remove the child component TodoItem when a button inside it is clicked according to it's key, so it would remove that item from the state of the parent component AddComponent.
The problem i'm facing is i can't change the state anywhere else than the render() function, because there is where i'm dynamically generating the TodoItem components using the state.
And changing the state inside the render() function would result in an infinite loop. Please help how to resolve this problem.
I'm using Bootstrap 4 using a CDN for styling.
AddComponent.js
import React from 'react'
import shortid from 'shortid'
import TodoItem from './TodoItem';
class AddComponent extends React.Component {
constructor() {
super();
this.state = {
todoText: '',
todoList: []
}
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleTodoClick = this.handleTodoClick.bind(this);
}
handleChange(e) {
e.persist();
this.setState((prevState) => {
return {
todoText: e.target.value,
todoList: prevState.todoList
}
})
}
handleClick() {
this.setState((prevState) => {
return {
todoText: prevState.todoText,
todoList: [{
text: prevState.todoText,
id: shortid.generate()
}, ...prevState.todoList]
}
})
}
handleTodoClick(id) {
const newState = this.state.todoList.filter(todoL => todoL.id === id);
this.setState({ todoList: newState });
}
render() {
const todos = this.state.todoList.map((todo) => {
return (
<TodoItem key={todo.id} value={todo.text} click={this.handleTodoClick(todo.id)}>{todo.text}</TodoItem>
)
})
return (
<div>
<div className="row">
<div className="col-sm-9">
<div className="form-group">
<input
type="text"
className="form-control"
placeholder="Add a Todo"
onChange={this.handleChange}
/>
</div>
</div>
<div className="col-sm-3">
<button
type="button"
className="btn btn-primary btn-block"
onClick={this.handleClick}
>Add
</button>
</div>
</div>
<div>
{todos}
</div>
</div>
)
}
}
export default AddComponent
TodoItem.js
import React from 'react'
class TodoItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
onClick={this.props.click}
data-dismiss="alert">×</button>
{this.props.value}
</div>
)
}
}
export default TodoItem
Try : https://codesandbox.io/s/hopeful-moon-duqfe
There were two problems; 1st one is filter and the 2nd one is handler callback function that is set to click prop.
1st problem : Filter function should filter that does not match your id so you should use NOT equal :
handleTodoClick(id) {
const newState = this.state.todoList.filter(todoL => todoL.id !== id);
this.setState({ todoList: newState });
}
2nd problem : When you set click prop as a handler callback function, you should set reference of the function itself without calling it. Not like this : click={this.handleTodoClick(todo.id). This will call the function and set the click prop to result of it, which is undefined since it does not return to anything.
So i have changed your click props as click={this.handleTodoClick}
const todos = this.state.todoList.map(todo => {
return (
<TodoItem key={todo.id} id={todo.id} value={todo.text} click={this.handleTodoClick}>
{todo.text}
</TodoItem>
);
});
On child component, onClick is similary set to function itself onClick={() => this.props.click(this.props.id)} NOT like onClick={this.props.click(this.props.id)}.
import React from "react";
class TodoItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
onClick={() => this.props.click(this.props.id)}
data-dismiss="alert"
>
×
</button>
{this.props.value}
</div>
);
}
}
Fix:
<TodoItem key={todo.id} todoId={todo.id} value={todo.text} click={this.handleTodoClick(todo.id)}>{todo.text}</TodoItem>
We can't use key as id. The key property is used by React under the hood, and is not exposed to us.
import React from 'react'
class TodoItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
// look here
onClick={e => {this.props.click(this.props.todoId)}}
data-dismiss="alert">×</button>
{this.props.value}
</div>
)
}
}
export default TodoItem
Try the following by updating the handle for click in AddComponent then creating a handler within TodoItem that calls props click with the respective todo item id value. I'd recommend to just pass the entire todo so that you can access both the unique id and value in TodoItem:
AddComponent:
const todos = this.state.todoList.map((todo) => {
return (
<TodoItem key={todo.id} todo={todo} click={this.handleTodoClick}>{todo.text}</TodoItem>
)
})
TodoItem:
import React from 'react'
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.click(this.props.todo.id);
}
render() {
return (
<div className="alert alert-secondary alert-dismissible fade show">
<button
type="button"
className="close"
onClick={this.handleClick}
data-dismiss="alert">×</button>
{this.props.todo.value}
</div>
)
}
}
export default TodoItem;

open a html modal programmatically in React

I have this two elements a button and a dialog
<dialog className='w-11/12 shadow-none rounded-tl-md rounded-tr-md lg:rounded-lg absolute'>wqdwe</dialog>
<button className=" px-6 py-2 rounded absolute mt-12 ml-12" onClick={} >Click</button>
How can I open the dialog on clicking the button in React
constructor(props) {
super(props);
this.myRef = React.createRef();
}
showModals(){
this.myRef.showModal();
}
componentDidMount() {
//this.showModals()
}
EDIT: I am trying to access the .showModal() method in the dialog according to MDN https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog. How can I do that, I need the dimmed background feature when the modal is opened.
You do not need componentDidMount nor useRef with the state and using the props open of the dialog you can show it conditionally.
first solution using isOpen is the state
class Modal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<dialog style={{width: "80%", height: "80%", marginTop: 10, backgroundColor: '#eee'}}
open={this.props.open}
>
<p>Greetings, one and all!</p>
</dialog>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
isOpen: false
};
}
switchModal = (prevState) => {
this.setState((prevState, props) => {
return { isOpen: !prevState.isOpen }
});
}
render() {
return (
<div>
<button onClick={this.switchModal}>
{this.state.isOpen ? 'Close' : 'Open'} Modal
</button>
<br/>
<Modal open={this.state.isOpen}/>
</div>
);
}
}
React.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script>
<div id="root"></div>
second solution using native showModal method. With this method you can use the css property dialog::backdrop.
class Modal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<dialog id='modal' style={{width: "80%", height: "80%", marginTop: 10, backgroundColor: '#eee'}}
>
<p>Greetings, one and all!</p>
<button onClick={this.props.closeModal}>
Close Modal
</button>
</dialog>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
isOpen: false
};
}
switchModal = (prevState) => {
this.setState((prevState, props) => {
if(!prevState.isOpen) {
document.getElementById('modal').showModal()
} else {
document.getElementById('modal').close()
}
return { isOpen: !prevState.isOpen }
});
}
render() {
return (
<div>
{!this.state.isOpen && <button onClick={this.switchModal}>
Open Modal
</button>}
<br/>
<Modal
closeModal={this.switchModal}
/>
</div>
);
}
}
React.render(<App />, document.getElementById('root'));
dialog {
height: 80%;
width: 80%
}
dialog::backdrop {
background: rgba(255,0,0,.25);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script>
<div id="root"></div>
You can use the React state API to show and hide components based on actions taken elsewhere in the code.
class MyComponent extends React.Component {
constructor() {
this.state = {
isDialogVisible: false
}
}
handleClick = () => {
this.setState({ isDialogVisible: !this.state.isDialogVisible })
}
render() {
const { isDialogVisible } = this.state
return (
<div>
<Button onClick={this.handleClick}>{isDialogVisible ? 'Hide' : 'Show'} dialog</Button>
{this.state.isDialogVisible && <Dialog />}
</div>
)
}
}

How to access method of the child component from parent in reactjs

I'm trying to call child component from parent component in reactjs using refs.but it throws error saying showModal() is not a function when I tried to call.
//app.js
class app extends Component {
constructor(props) {
super(props);
this.POPUP = React.createRef();
}
showModal(){
this.POPUP.showModal(true);
}
render() {
return (
<React.Fragment>
<span><a onClick={() => this.showModal()}>Show</a></span>
<POPUP onRef={ref => (this.POPUP = ref)}></POPUP>
</React.Fragment >
)
}
}
popup.js
class POPUP extends Component {
showModal(show) {
console.log('showmodal');
}
render() {
console.log(this.props.showModalPopup);
<React.Fragment>
<Modal
position="center">
<div>
//code
</div>
</Modal>
</React.Fragment>
)
}
}
Is there any alternative in nextjs.please help
https://reactjs.org/docs/refs-and-the-dom.html#accessing-refs
First of all if you want to access that POPUP instance you should do
this.POPUP.current.showModal(true);
BTW Your showModal function needs to be bound to the child component if you intend to alter its state.
However, even this is doable - this is usually not the recommended way of doing React.
If you want the parent to decide if showModalPopup should be true, you probably should keep the state inside of your parent component:
class App extends Component {
constructor(props) {
super(props);
this.state = { showModalPopup: false };
this.showModal = this.showModal.bind(this);
}
showModal(){
this.setState({ showModalPopup: true });
}
render() {
return (
<React.Fragment>
<span><a onClick={this.showModal}>Show</a></span>
<POPUP show={this.state.showModalPopup}></POPUP>
</React.Fragment >
)
}
}
const POPUP = ({ show }) => (
<Modal show={show} position="center">
// your content.
</Modal>
)

React: Open Modal from a different component

I have a Modal component in the Main.js app and I want to trigger it from a different component (in this case Homepage, but I have one component for each page).
I don´t know how to pass a component to be rendered inside the modal.
If it helps I´m using Context API.
App.js
const App = () => {
return (
<div>this is the main app</div>
<Modal />
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Modal.js
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
'open': false
}
}
componentWillReceiveProps(nextProps) {
this.setState({open: nextProps.open})
}
render() {
if (!this.state.open) {
return false
}
return(
<div className="modal">
{this.props.children}
</div>
)
}
}
export default Modal
Homepage.js
class Homepage extends Component {
constructor(props) {
super(props)
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
// here I want to open the modal and pass the <ModalContent /> component
// basically call the <Modal open="true"> from the Main component
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}
thank you.
I strongly recommend using something like react-modal (npm). It allows you to keep modal content right next to the trigger. It does this by appending a "portal" high up in the DOM and handles appending the content to is.
Your example may look like the following:
import Modal from 'react-modal';
class Homepage extends Component {
constructor(props) {
super(props)
this.state = { modalOpen: false };
this.handleOpenModal = this.handleOpenModal.bind(this)
}
handleOpenModal() {
this.setState({ modalOpen: true });
}
render() {
return(
<div className="homepage">
<button onClick={this.handleOpenModal}>open the modal</button>
<Modal open={this.state.modalOpen}>
<ModalContent />
</Modal>
</div>
)
}
}
const ModalContent = () => {
return(
<div>this is the content I want to render inside the modal</div>
)
}

React child element calling the parent element, on value

I'm trying to create a MessageBox(Modal Box) element which gets inputs to form the Modal Box, The close method in MessageBox somehow doesn't call the parent close and inturn get the Modal disappear, any help please ??
export default class MessageBox extends Component{
constructor(props) {
super(props);
this.close = this.close.bind(this);
}
close(){
this.props.callbackParent;
}
render(){
return (
<div>
<Modal show={this.props.visibility} onHide={this.close}>
<ModalHeader closeButton>
<ModalTitle>{this.props.msgHeader}</ModalTitle>
</ModalHeader>
<ModalBody>{this.props.msgBody}</ModalBody>
</Modal>
</div>
);
}
}
export default class Product extends Component {
constructor(props) {
super(props);
this._initState = {
showModal: false
}
this.state = this._initState;
this.open = this.open.bind(this);
this.close = this.close.bind(this);
}
open(){
this.setState(
{ showModal: true }
);
}
close(){
this.setState(
{ showModal: false }
);
}
render(){
return (
<div>
<Button bsStyle="primary" bsSize="large" onClick={this.open}>
Go!!!
</Button>
<MessageBox visibility={this.state.showModal} msgHeader='Header goes here ...' msgBody='Overlay message body goes here ..' callbackParent={this.close}/>
</div>
);
}
};
It seems you are missing the parenthesis on in the close method of MessageBox.
this.props.callbackParent;
should be
this.props.callbackParent();
(thx to #azium) for the answer.
I got it working by this
export default class MessageBox extends Component{
constructor(props) {
super(props);
}
render(){
return (
<div>
<Modal show={this.props.visibility} onHide={this.props.callbackParent}>
<ModalHeader closeButton>
<ModalTitle>{this.props.msgHeader}</ModalTitle>
</ModalHeader>
<ModalBody>{this.props.msgBody}</ModalBody>
<ModalFooter>
<Button onClick={this.props.callbackParent}>Close</Button>
</ModalFooter>
</Modal>
</div>
);
}
}

Resources