im trying to use the react-model component but bootstrap is messing it. When i put it outside the div with col class it works fine but separaiting it messes up the layout. Here the component in which i use the modal.
class BeerItem extends Component {
constructor(props){
super(props);
this.state = {
showModal: false,
};
}
handleOpenModal = () => {
this.setState({ showModal: true });
}
render(){
Modal.setAppElement('#root');
return (
<div key={this.props.beer.id} className="col-sm-8 col-md-4 m-3 beer-item col-xl-3 text-center" onClick={this.handleOpenModal}>
<div className="d-flex align-items-center justify-content-center hvr-rotate">
<div className="img-container">
<img src={this.props.beer.image_url} alt=""/>
</div>
<div className="beer-nametag">
<p className="h5 font-weight-bold beer-card-name">{this.props.beer.name}</p>
<p className="text-secondary">{this.props.beer.tagline}</p>
</div>
</div>
<Modal
className="modal"
isOpen={this.state.showModal}
onRequestClose={ () => this.setState({ showModal: false }) }
shouldCloseOnOverlayClick={true}
ariaHideApp={false}
>
{this.props.beer.name}
</Modal>
</div>
)
}
}
This is an item component which is rendered in a list component. I tried to put the modal in the list component but then i cant figure how to pass the beer id into it. The list is in a infinity scroll component and when i want to update the state in the list by passing a function to item component which takes the id of clicked beer i get an error with maximum depth exceed...
You can use <React.Fragment>, so the you doesn't wrap the components with an extra <div>.
Many modal render outside the view window when it's closed (For animation purpose). However, you might want to completely remove it from the DOM after close event.
The tested code. Notice, the Modal is rendered conditionally based on this.state.showModal:
const MyModal = props => (
<Modal
className="modal"
isOpen
onRequestClose={props.onClose}
shouldCloseOnOverlayClick
ariaHideApp={false}
>
{props.beer.name}
</Modal>
);
class BeerItem extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false,
};
}
handleOpenModal = () => {
this.setState({ showModal: true });
};
onModalClose = () => {
this.setState({ showModal: false });
};
render() {
return (
<React.Fragment>
<div onClick={this.handleOpenModal}>Click me</div>
{this.state.showModal && <MyModal onClose={this.onModalClose} beer={{name: "Beer"}} />}
</React.Fragment>
);
}
}
Related
I'm trying to create a modal that asks users if they're an individual or organization, and then shows a sign up modal specific to that type of user. This is what I have so far:
parent:
this.state = {
showInd: false,
showOrg: false,
};
changeInd = () => {
this.setState({
showInd: !this.state.showInd
});
this.props.onClose(); //this closes the initial modal
}
//exact same syntax for changeOrg
render(){
return(
<div onClick={this.changeInd}>
<FontAwesomeIcon icon={faUser} className="fa-7x icon"/>
<span>individual</span>
</div>
<div onClick={this.changeOrg}>
<FontAwesomeIcon icon={faUsers} className="fa-7x icon"/>
<span>organization</span>
</div>
<SignUpInd show={this.state.showInd} />
<SignUpOrg show={this.state.showOrg} />
)}
and the child:
render(){
if (this.props.show){
return(
<various sign up html>
)}
}
The parent component is re-rendering when the state changes, but the child component is not, even though the props are changing. I've tried using componentDidUpdate, but that is also never triggered when the props change here.
What could I be doing wrong?
EDIT: So I've realized that if I comment out the line that closes the initial modal with a callback function, the signUpInd modal will render properly. Why can I not do both?
this works:
import React from "react";
import SignUpInd from "./SignUpInd";
import SignUpOrg from "./SignUpOrg";
import "./styles.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showInd: false,
showOrg: false
};
}
showInd = () => {
this.setState((state) => ({ showInd: !state.showInd }));
};
showOrg = () => {
this.setState((state) => ({ showOrg: !state.showOrg }));
};
render() {
return (
<React.Fragment>
<div onClick={this.showInd}>
<FontAwesomeIcon icon={faUser} className="fa-7x icon"/>
<span>individual</span>
</div>
<div onClick={this.showOrg}>
<FontAwesomeIcon icon={faUsers} className="fa-7x icon"/>
<span>organization</span>
</div>
<SignUpInd show={this.state.showInd} />
<SignUpOrg show={this.state.showOrg} />
</React.Fragment>
);
}
}
1.At the parent component use a function that changes the state.
state = {
showInd: false,
showOrg: false,
};
stateChange = () =>{
this.setState({showInd:!this.state.showInd})
}
2.Use an onClick function on the div it will give opposite value of what it is right now and pass it as a props to the next component
<div onClick={this.stateChange}> //this onClick just flips showInd to the opposite of what it is currently - that's working properly
<span>individual</span>
</div>
<SignUpInd show={this.state.showInd} stateChange= {this.stateChange} />
3.At the other end just recieve the props and console log it
const {show,stateChange} = this.props
console.log(show);
I am making a dashboard component which displays rendered previews and code for HTML snippets. Inside of the dashboard component I am mapping the array of snippets using .map. Each mapped snippet is going to have a delete function (already built) and an update function.
For the update function to work each snippet has it's own child modal component. I need to pass the ID of the snippet to the modal component where I can combine the ID with the new content before updating the database and state.
However, I'm making a mistake somewhere as I pass the ID as props to the modal.
.map used inside of my Dashboard.js Dashboard class component.
{this.state.snippets.map(snippet => (
<>
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p> //THIS WORKS
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button type="button" onClick={this.showModal}>
Open
</button>
</div>
</div>
</div>
<Modal
sid={snippet._id} //PASS ID HERE
show={this.state.show}
handleClose={this.hideModal}
></Modal>
</>
))}
This renders the snippets below (3 snippet pods, with their database ID included).
The open button opens the modal (Modal.js) below.
import React, { Component } from 'react'
import api from '../api'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
handleInputChange = event => {
this.setState({
[event.target.name]: event.target.value,
})
}
handleClick = id => event => {
event.preventDefault()
console.log(id)
}
render() {
const { sid, show, handleClose } = this.props
console.log(sid)
const showHideClassName = show ? 'modal display-flex' : 'modal display-none'
return (
<div id="Modal" className={showHideClassName}>
<div id="modal-main">
<h4>Edit snippet {sid}</h4>
<form>
Name:{' '}
<input
type="text"
value={this.state.name}
name="name"
onChange={this.handleInputChange}
/>{' '}
<br />
Details:{' '}
<input
type="text"
value={this.state.details}
name="details"
onChange={this.handleInputChange}
/>{' '}
<br />
Content:{' '}
<textarea
value={this.state.content}
name="content"
cols="30"
rows="10"
onChange={this.handleInputChange}
/>{' '}
<br />
<button onClick={this.handleClick(sid)}>TEST ME</button>
</form>
<button onClick={handleClose}>Close</button>
{this.state.message && (
<div className="info">{this.state.message}</div>
)}
</div>
</div>
)
}
}
The console.log just under the render actually pastes the correct 3 ID's the console.
However, calling the ID (sid) within the Modal.js return will only show the last snippet ID, no matter which Modal I open. The same goes for pushing that ID to the handleClick function where I intend to combine the ID with an update package.
Solution below as initiated by HMR in the comments.
The problem was all the modals were showing and just the last one was visible.
Fixed by moving the modal out of the .map and instead updating the ID from within the .map to the state and passing the state ID to a new nested component within the modal.
Also switched to using dynamic CSS to show and hide the modal based on the state.
Dashboard.jsx
export default class Snippets extends Component {
constructor(props) {
super(props)
this.showModal = React.createRef()
this.state = {
snippets: [],
show: false,
sid: '',
}
}
handleDelete = id => event => {
event.preventDefault()
api
.deleteSnippet(id)
.then(result => {
console.log('DATA DELETED')
api.getSnippets().then(result => {
this.setState({ snippets: result })
console.log('CLIENT UPDATED')
})
})
.catch(err => this.setState({ message: err.toString() }))
}
handleModal = id => {
this.setState({ sid: id })
this.showModal.current.showModal()
}
//<div id="preview">{ReactHtmlParser(snippet.content)}</div>
render() {
return (
<>
<Modal ref={this.showModal} handleClose={this.hideModal}>
<ModalUpdate sid={this.state.sid} />
</Modal>
<div className="Dashboard">
<div className="wrapper">
<div className="container">
<div className="holder">
<div className="content">
<div className="content__body">
<h3>Dashboard</h3>
</div>
</div>
</div>
<div className="break"></div>
{this.state.snippets.map(snippet => (
<div key={snippet._id} className="holder--pod">
<div className="content">
<div className="content__snippet-preview">
Snippet preview
</div>
<div className="content__body">
<h4>{snippet.name}</h4>
<p>{snippet.details}</p>
<p>{snippet._id}</p>
<pre>
<code>{snippet.content}</code>
</pre>
</div>
<div className="content__button">
<button onClick={this.handleDelete(snippet._id)}>
Delete
</button>
<button
type="button"
onClick={() => this.handleModal(snippet._id)}
>
Open
</button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</>
)
}
Modal.jsx
import React, { Component } from 'react'
export default class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
}
showModal = () => {
this.setState({ show: true })
}
hideModal = () => {
this.setState({ show: false })
}
render() {
return (
<div
id="Modal"
style={{ display: this.state.show === true ? 'flex' : 'none' }}
>
<div id="modal-main">
<h4>Edit snippet </h4>
{this.props.children}
<button onClick={() => this.hideModal()}>Close</button>
</div>
</div>
)
}
}
ModalUpdate.jsx
import React, { Component } from 'react'
export default class ModalUpdate extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
details: '',
content: '',
message: null,
}
}
// handleInputChange = event => {
// this.setState({
// [event.target.name]: event.target.value,
// })
// }
// handleClick = id => event => {
// event.preventDefault()
// console.log(id)
// }
render() {
return <h4>ID = {this.props.sid}</h4>
}
}
I am not sure about the handleDelete function,. but replacing the line should solve the issue probably
<button onClick={() => this.handleDelete(snippet._id)}>
One potential issue is the this.handleDelete(snippet._id) will fire immediately rather than onClick, so you will need to add an anonymous function in the event listener:
() => this.handleDelete(snippet._id)
instead of
this.handleDelete(snippet._id)
I have a Parent component, App.js and a Child component, MealModal.js. When a user click on a specific meal card, it raises a modal that should display further information about the meal.
Hence I try to find a way to dynamically change the modals's data, depending on which meal card is clicked
I have tried to pass the id of the meal to the onClick={this.openModal} function and set the state of modalId is the function. But I got the following error:
Warning: Cannot update during an existing state transition (such as
within render or another component's constructor). Render methods
should be a pure function of props and state; constructor side-effects
are an anti-pattern, but can be moved to 'componentWillMount'.
Here are my components so far:
App.js:
import React from 'react';
import MealCard from './MealCard';
import MealsMap from './MealsMap';
import MealsFilters from './MealsFilters';
import MealModal from './MealModal';
export default class App extends React.Component {
constructor() {
super();
this.state = {
modalIsOpen: false,
modalId: 0
}
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
};
openModal() {
this.setState({modalIsOpen: true});
};
closeModal() {
this.setState({modalIsOpen: false});
};
render() {
return (
<div>
<MealsFilters/>
<div className="app-wrapper" style={{display: 'flex'}}>
<div className="container">
<div className="row">
{[...Array(20)].map((x, i) =>
<div className="col-sm-6 col-xs-12 " key={i} onClick={this.openModal}>
<MealCard />
</div>
)}
</div>
</div>
<MealsMap/>
</div>
<MealModal modalIsOpen={this.state.modalIsOpen} closeModal={this.closeModal} modalId={this.state.modalId}/>
</div>
);
}
}
MealModal.js
import React from 'react';
import Modal from 'react-modal';
const customStyles = {
content : {
}
};
Modal.setAppElement('#app')
export default class MealModal extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<Modal
isOpen={this.props.modalIsOpen}
onRequestClose={this.props.closeModal}
style={customStyles}
contentLabel="Meal Modal"
>
<div className="modal-wrapper">
<div className="container text-center">
<h1>Hello</h1>
<h2>ID of this modal is {this.props.modalId}</h2>
<h3>This is an awesome modal.</h3>
<button onClick={this.props.closeModal}>close</button>
</div>
</div>
</Modal>
)
}
}
Any idea on how I could do this ?
Ok so I found the solution:
First, I changed onClick={this.openModal} in the parent comoponent to onClick= () => {this.openModal}
Second, I add the id as a parameter:
onClick= () => {this.openModal(i)}
Finally: update the openModal function:
openModal(modalId) {
this.setState({modalIsOpen: true,
modalId});
};
And it works.
openModal(modalId) {
this.setState({
modalId,
modalIsOpen: true
});
};
and modify the call function as
<div className="col-sm-6 col-xs-12" key={i} onClick={() => this.openModal(x) } >
<MealCard/>
</div>
I have a component built using the below code. The aim is to add a class on the card to highlight it when the button inside it is clicked. However, the below code works on the first click but doesn't work for the subsequent clicks.
I understood that I have to set the clicked state of other elements to false when I remove the class. How can this be done?
import React, { Component } from 'react';
import './PricingCard.css';
class PricingCard extends Component {
constructor(){
super();
this.state = {
clicked : false
}
}
makeSelection(){
let elems = document.getElementsByClassName('Card');
for(var i=0;i<elems.length;i++){
elems[i].classList.remove("active");
}
this.setState({clicked: true});
}
render() {
var activeClass = this.state.clicked ? 'active' : '';
return (
<div className= {"categoryItem Card " + this.props.planName + " " +activeClass}>
<div className="cardDetails">
<div> {this.props.planName} </div>
<div className="pricing"> {this.props.price} </div>
<button onClick={this.makeSelection.bind(this)} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {this.props.footerText} </div>
</div>
</div>
);
}
}
export default PricingCard;
Wouldn't it be easier to have the logic in a parent component? Since it is "aware" of all the child Card components.
Have something like...
this.state = { selectedComponent: null };
onClick(card_id) {
this.setState({ selectedComponent: card_id });
}
...in render:
const cards = smth.map((card) =>
<Card onClick={this.onClick.bind(this, card.id)}
isActive={map.id === this.state.selectedComponent} />
Would this work?
Best way will be to lift lift the state up. Like this:
class PricingCardContainer extends React.Component {
constructor(props){
super(props);
this.state = {
selectedCard: NaN,
}
}
handleCardClick(selectedCard){ this.setState({ selectedCard }); }
render() {
return (
<div>{
this.props.dataArray.map((data, i) =>
<PricingCard
key={i}
className={this.state.selectedCard === i ? 'active': ''}
price={data.price}
onClick={() => this.handleCardClick(i)}
footerText={data.footerText}
planName={data.planName}
plan={data.plan}
/>
)
}</div>
)
}
}
const PricingCard = ({ className = '', planName, price, onClick, footerText }) => (
<div className= {`categoryItem Card ${planName} ${className}`}>
<div className="cardDetails">
<div> {planName} </div>
<div className="pricing"> {price} </div>
<button onClick={onClick} className="buttonPrimary"> Select this plan </button>
<div className="subtitle"> {footerText} </div>
</div>
</div>
);
export default PricingCard;
Although it would be better to use some data id than index value.
I am trying to code a react component for a modal and getting the error: "Uncaught TypeError: this.setState is not a function" when the openModal() is called. Below is my code:
class Header {
constructor() {
this.state = { showModal: false };
}
openModal() {
this.setState({showModal: true});
}
closeModal() {
this.setState({showModal: false});
}
render() {
return (
<div className="Header">
<ModalDialog heading="heading" show={this.state.showModal}>
<p>Body</p>
</ModalDialog>
<div className="Header-container">
<Navigation className="Header-nav" />
<div className="Navigation2">
<Button bsStyle='primary' onClick={this.openModal.bind(this)}>Sign in</Button>
<Button bsStyle='info' href="/register" onClick={Link.handleClick}>Sign up</Button>
</div>
</div>
</div>
);
}
}
class ModalDialog {
render() {
if (this.props.show) {
return (
<div className="Overlay">
<Modal heading={this.props.heading}>
<div>{this.props.children}</div>
</Modal>
</div>
);
}
return null;
}
};
class Modal {
render() {
return (
<div className="Modal effect">
<h3>{this.props.heading}</h3>
<div className="content">
{this.props.children}
</div>
</div>
);
}
};
I am also trying to pass in a className into the element <div className="Modal effect"> from the Header component to change the dimensions of the Modal, is this possible?
Would be great if somebody could help me out with this. I am just starting out with React. Thanks
I am also trying to pass in a className into the element from the Header component to change the dimensions of the Modal, is this possible?
This is typically what props are made for, passing static information such size.
So simply pass your class/size/staticprops as a prop like this:
<ModalDialog size={//whatever you need} className="Modal effect"/>
From ModalDialog pass it to Modal
<Modal size={this.props.size} />
And then you just have to use it inside your Modal for whatever purpose you want. More about props in general :https://facebook.github.io/react/docs/transferring-props.html
Hope it helps
I'm not familiar with reactJS but this looks wrong to me:
constructor() {
this.state = { showModal: false };
}
Should it be:
constructor() {
this.setState = ({ showModal: false });
}
?
Otherwise your Header class sets its state attribute to { showModal: false }
Which obviously has no 'setState' member function.