I have a problem with update props.
I have two components.
The first is the App, where there is a button that opens or closes the modal.
The second is the modal structure.
The first component stores the state, I click the buton, the state changes, I send props to ModalComponent component (the modal is opening).
And in this component should change this state if I want to close modal.
But if I click on the Cancel button in modal, nothing happens.
The state does not change.
How to change it, how it would make communication between the child and the parent?
This is my code :
ModalComponent
import React, { Component } from "react";
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
class ModalComponent extends Component {
constructor(props) {
super(props);
this.state = {
modal: props.modal
};
this.toggle = this.toggle.bind(this);
}
toggle() {
const { modal } = this.state;
this.setState({
modal: !modal
});
}
render() {
const { modal } = this.props;
return (
<div>
<Modal isOpen={modal} toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Halo</ModalHeader>
<ModalFooter>
<Button onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default ModalComponent;
App
import React from "react";
import ReactDOM from "react-dom";
import Modal from "./modal";
import { Button } from "reactstrap";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
const { modal } = this.state;
this.setState({
modal: !modal
});
}
render() {
const { modal } = this.state;
return (
<div>
<Button
type="link"
onClick={this.toggle}
>
Delete account
</Button>
<Modal modal={modal} />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The state in your Modal Component would change but you aren't using it to set the isOpen props on Modal component.
Also you must not use state which is directly derivable from props. You must instead pass a handler from Parent to update the state in the parent itself
ModalComponent
import React, { Component } from "react";
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
class ModalComponent extends Component {
render() {
const { modal } = this.props;
return (
<div>
<Modal isOpen={modal} toggle={this.toggle}>
<ModalHeader toggle={this.props.toggle}>Halo</ModalHeader>
<ModalFooter>
<Button onClick={this.props.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default ModalComponent;
App
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
const { modal } = this.state;
this.setState({
modal: !modal
});
}
render() {
const { modal } = this.state;
return (
<div>
<Button
type="link"
onClick={this.toggle}
>
Delete account
</Button>
<Modal modal={modal} toggle={this.toggle}/>
</div>
);
}
}
The way you have it now, the modal is setting its own state, but it is still receiving a prop from the parent, which is keeping it open.
Here's an example of one way to do it. Note that both opening and closing is dealt with using the state of the parent, not the state of the child.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = { open: false };
toggle = () => {
this.setState({ open: !this.state.open });
};
render() {
return (
<React.Fragment>
something
<Modal show={this.state.open} />
<Child toggle={this.toggle} />
</React.Fragment>
);
}
}
const Child = ({ toggle }) => {
return <button onClick={toggle}>toggle</button>;
};
const Modal = ({ show }) => {
if (show) {
return <h1>I am a modal</h1>;
}
return null;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
CodeSandbox here.
Related
onmouse is not working when I hover my mouse on the text in h1 tag, but its printing value in the console when I click on it.
import React, {
Component
} from 'react';
class App extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick = (event) => {
let word = event.target.innerText;
console.log(word)
}
render() {
return (<div>
<div className="App">
<h1 onMouseOver = {this.handleClick}>hover Me</h1>
</div>
</div>);
}
}
export default App;
the code works fine, only the "super(props)" part has been deprecated, another trick: when you use Arrow functions you don't need to bind the function in the constructor. This is the complete code:
import React, { Component } from 'react';
class YourComponent extends Component {
constructor(props) {
super();
this.state={};
}
handleClick = (event) => {
let word = event.target.innerText;
console.log(word)
}
render() {
return (
<div>
<div className="App">
<h1 onMouseOver={this.handleClick}> hover Me </h1>
</div>
</div>
);
}
}
export default YourComponent;
Enjoy ;)
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>
)
}
I'm using conditional rendering to render a specific button, if the state.show = true.
The problem is, that, if show is not true, the anim is played, but the component is not removed (because the anim doesn't remove the component, it's just animating it.)
i'm using Material ui, aphrodite, react-magic
there's my code :
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Button from '#material-ui/core/Button'
import {StyleSheet, css} from 'aphrodite'
import { swap, vanishOut } from 'react-magic'
import vanishIn from 'react-magic/lib/bling/vanishIn';
const styles = StyleSheet.create({
magic: {
animationName: vanishIn,
animationDuration: '2s'
},
magicOut: {
animationName: vanishOut,
animationDuration: '2s'
}
});
class App extends Component {
constructor(props) {
super(props);
this.state = {show: true};
}
FalseState(){
this.setState({show:false});
}
render() {
const show = this.state.show
let buttonStart;
if(show===true){
buttonStart =
<div className={css(styles.magic)}>
<Button className="start" variant="raised" onClick={() => this.FalseState()}>Button</Button>
</div>;
} else {
buttonStart =
<div className={css(styles.magicOut)}>
<Button className="start" variant="raised" >Button</Button>
</div>;
}
return (
<div className="App">
{buttonStart}
</div>
);
}
}
export default App;
I tried to use another state variable to trigger a unmount of the component after the animation is over - using setTimeout.
Hope this helps :)
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import Button from '#material-ui/core/Button';
import { StyleSheet, css } from 'aphrodite';
import { swap, vanishOut } from 'react-magic';
import vanishIn from 'react-magic/lib/bling/vanishIn';
const styles = StyleSheet.create({
magic: {
animationName: vanishIn,
animationDuration: '2s'
},
magicOut: {
animationName: vanishOut,
animationDuration: '2s'
}
});
class App extends Component {
constructor(props) {
super(props);
this.state = { show: true, unMount: false };
}
FalseState() {
this.setState({ show: false }, () => {
setTimeout(() => {
this.setState({ unMount: true });
}, 2000);
});
}
render() {
const show = this.state.show;
let buttonStart;
if (this.state.unMount) {
return null;
}
if (show === true) {
buttonStart = (
<div className={css(styles.magic)}>
<Button className="start" variant="raised" onClick={() => this.FalseState()}>
Button
</Button>
</div>
);
} else {
buttonStart = (
<div className={css(styles.magicOut)}>
<Button className="start" variant="raised">
Button
</Button>
</div>
);
}
return <div className="App">{buttonStart}</div>;
}
}
export default App;
I want to make when i click button, show modal in different class.
how to make it this?
import React, { Component } from 'react';
import addmodal from './addmodal';
class page extends Component {
...//skip
handleAdd= () =>{
//...how?
}
render(){
return (
<button onClick={this.handleAdd} > Add </button>
)
}
}
import React, { Component } from 'react';
class addmodal extends Component {
// ... skip
render(){
return(
<modal inOpen={this.state.isopen} >
...//skip
</modal>
)
}
}
export default addmodal;
I can't call this addmodal...
how to call modal?
First, your variable naming is terrible. I fixed it for you here. The state that dictates if modal is open must be on the parent component since you have your trigger there. Then, pass it as props to the modal component. Here is the fixed code for you:
import React, { Component } from 'react';
import AddModal from './addmodal';
class Page extends Component {
constructor(){
super();
this.state = { isModalOpen: false };
}
...//skip
handleAdd= () =>{
this.setState({ isModalOpen: true });
}
render(){
return (
<div>
<button onClick={this.handleAdd} > Add </button>
<AddModal isOpen={this.state.isModalOpen} />
</div>
)
}
}
import React, { Component } from 'react';
class AddModal extends Component {
// ... skip
render(){
return(
<modal inOpen={this.props.isOpen} >
...//skip
</modal>
)
}
}
export default AddModal;
what about using trigger. Its very usefull.
<Modal trigger={ <button onClick={() => onCLickPay()} className={someting}> When clicked here modal with show up</button> }`enter code here` >
I need to render a modal/lightbox component dynamic into a list array component, but it only renders the last modal content.
How can I turn this modal component dynamic to call it from the main component and populate it with correct data from an object array?
My List component is:
import React, { Component } from 'react';
import LightBox from './LightBox';
class ListPrice extends Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
}
toggleModal = () => {
this.setState({
isOpen: !this.state.isOpen
});
}
render() {
return (
<div>
{this.props.products.map(product => {
return(
<div>
<a key={product.id} onClick={this.toggleModal}>
<h3>{product.title}</h3>
<p>{product.description}</p>
</a>
<LightBox key={product.id} show={this.state.isOpen}
onClose={this.toggleModal}>
{product.modalContent}
</LightBox>
</div>
);
})}
</div>
);
}
}
export default ListPrice;
And my LightBox component is (I removed styles to display short code here):
import React from 'react';
import PropTypes from 'prop-types';
class LightBox extends React.Component {
render() {
if(!this.props.show) {
return null;
}
return (
<div>
<div>
{this.props.children}
<div>
<button onClick={this.props.onClose}>
Close
</button>
</div>
</div>
</div>
);
}
}
LightBox.propTypes = {
onClose: PropTypes.func.isRequired,
show: PropTypes.bool,
children: PropTypes.node
};
export default LightBox;
Thank you for any advice :)
With show={this.state.isOpen} you always display all the modals - only the last one is visible as other modals are displayed behind it.
In order to fix that you must show only the selected dialog. You can store opened dialog in state with construct like this.setState({ openedDialog: product.id }).
Then you can query if the dialog is open by using this.state.openedDialog === product.id. That should do the job.
openModal = (id) = () => {
this.setState({
openedDialog: id
});
}
closeModal = () => {
this.setState({
openedDialog: null
});
}
show={this.state.openedDialog === product.id}
onClick={this.openModal(product.id)}
onClose={this.closeModal}