I have been working on my first Meteor application and am a bit stuck. I want to create my code following the latest guidelines (ES6 and React 15) but I am confused with all the recent changes in Javascript.
I want to add a Bootstrap Modal in my current comments list but can't seem to figure out how to add my content to the modal using the right up to date syntax.
Here is my current code:
In comment.js:
import React from 'react';
import { Row, Col, ListGroupItem, FormControl, Button } from 'react-bootstrap';
import { Bert } from 'meteor/themeteorchef:bert';
import { CommentsModal } from './comments-modal'
export const Comment = ({ comment }) => (
<ListGroupItem key={ comment._id }>
<Row>
<Col xs={ 8 } sm={ 10 }>
<FormControl
type="text"
defaultValue={ comment.title }
/>
</Col>
<Col xs={ 4 } sm={ 2 }>
<Button
bsStyle="danger"
className="btn-block">
Remove Comment
</Button>
</Col>
</Row>
<CommentsModal/>
</ListGroupItem>
);
In Comments-modal.js:
import React, { Component } from 'react';
import { Modal, Button, Tooltip } from 'react-bootstrap';
export class CommentsModal extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false,
};
this.close = this.close.bind(this);
this.open = this.open.bind(this);
}
close() {
this.setState({ showModal: false });
}
open() {
this.setState({ showModal: true });
}
render() {
return (
<div>
<Button
bsStyle="primary"
bsSize="large"
onClick={this.open}
>
</Button>
<Modal show={this.state.showModal} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title >Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Text in a modal</h4>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.close}>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
And last comments-list.js:
import React from 'react';
import { ListGroup, Alert } from 'react-bootstrap';
import { Comment } from './comment';
export const CommentsList = ({ comments }) => (
comments.length > 0 ? <ListGroup className="comments-list">
{comments.map((com) => (
<Comment key={ com._id } comment={ com } />
))}
</ListGroup> :
<Alert bsStyle="warning">No comments yet. Please add some!</Alert>
);
CommentsList.propTypes = {
comments: React.PropTypes.array,
};
I manage to get the Modal to show up and work but when I want to display data in it, I can't get it to work. What is the best way to combine both these into one?
Pass the data in props to the CommentsModal and render it as you would normally do.
I try to keep local state out of component when using redux if possible, so to answer your question on making it stateless, I would take the following steps:
Remove the button that opens the modal from the modal.js itself
Remove the actual modal from the modal.js, just put the modal content inside of there.
Change the open modal button to hook into an action creator that sets a prop to open the modal and passes it's content (also set one to close it)
So that looks something like this
<ListGroupItem key={ comment._id }>
<Row>
<Col xs={ 8 } sm={ 10 }>
<FormControl
type="text"
defaultValue={ comment.title }
/>
</Col>
<Col xs={ 4 } sm={ 2 }>
<Button
bsStyle="danger"
className="btn-block">
Remove Comment
</Button>
</Col>
</Row>
<!-- Here is where it changes, -->
<Button
bsStyle="primary"
bsSize="large"
onClick={this.props.openModal(comment)}
>
</Button>
<Modal show={this.props.commentModal} onHide={this.props.closeModal}>
<CommentsModal content={this.props.commentModal} />
</Modal>
Keep in mind, these naming conventions are just for examples sake : use whatever works best for you.
So what happens here is when you click that button you fire this.props.openModal (an action) which does something like this in the reducers -
case actions.OPEN_COMMENT_MODAL:
return state.set('commentModal', action.content);
the close buttons fire the onHide which is linked to the this.props.closeModal action which just does:
case actions.OPEN_COMMENT_MODAL:
return state.set('commentModal', undefined);
So what this allows you to do is have just 1 modal instance and you pass the current comment to it with that button click and open it. The show just checks the truthy value, so you set it back to undefined and it will hide itself.
Then I am passing the prop of content to the modal, so you can then use it inside the modal itself. Again, change the names to whatever works best for you.
Related
How do I show a modal box on page load using Chakra UI?
I've tried to find this information on Chakra's documentation without success.
import {
useDisclosure,
Modal,
ModalOverlay,
ModalContent,
ModalCloseButton,
ModalBody,
Text,
} from "#chakra-ui/react"
export default function BasicUsage() {
const { isOpen, onClose } = useDisclosure()
return (
<>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalCloseButton />
<ModalBody>
<Text>Hello</Text>
</ModalBody>
</ModalContent>
</Modal>
</>
)
}
You can pass a defaultIsOpen property to the useDisclosure hook to set the default value for isOpen.
Set that property to true to have the modal open on page load.
const { isOpen, onClose } = useDisclosure({ defaultIsOpen: true })
I've seen a few other questions with the same error but they don't seem to solve my issue.
I have a react-bootstrap modal that im trying to open from a button inside a child component but I get the following error.
This is the parent component:
OnboardPage.jsx
import React from 'react'
import { Row } from 'react-bootstrap'
import { PersonalDetails } from './personalDetails'
import { EmailVerification } from './emailVerification'
import { Form } from './form'
import { FAQs } from './faq'
import { LeftCol, RightCol } from './styles'
import { Modal, Button } from 'react-bootstrap'
const OnboardPage = props => {
const [show, setShow] = React.useState(false);
const handleClose = (showValue) => setShow(false);
const handleShow = (showValue) => setShow(true);
return (
<Row>
<LeftCol md={8}>
<PersonalDetails parentShowFn={handleShow}/>
<Form />
</LeftCol>
<RightCol md={4}>
<EmailVerification />
<FAQs />
</RightCol>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</Row>
)
}
export default OnboardPage
Child Component:
Personaldetails.jsx
import React from 'react'
import { colors } from '../../../../res'
import { TitleText, CommonText, SubHeadingText } from '../../../commons/freelancer/texts'
import { Container, TitleRow, DetailsRow, DetailsItem, EditBtn } from './personalDetailsStyles'
import { Modal, Button } from 'react-bootstrap'
// import EditDetailsModal from './EditDetailsModal'
const PersonalDetails = ({parentShowFn}) => {
return (
<Container>
<TitleRow>
<TitleText>Personal Details</TitleText>
<EditBtn onClick={parentShowFn()}>Edit</EditBtn>
</TitleRow>
</Container>
)
}
export default PersonalDetails
I've seen the stack trace and i believe the problem lies here:
But I can't figure out what it is :/
There is something wrong with the Edit Button onClick prop
<Button onClick={parentShowFn()}>Edit</Button>
What this does is: on each render of PersonalDetails, this will just set the state for the show state back in OnboardPageto true, therefore when the Modal onHide event is fired, state updates will cause re-renders, PersonalDetails will be re-rendered and therefore set the state of show to true again due to the above code
Furthermore, handleShow does not return anything therefore subsequent clicks on the Edit Button will not do anything (i.e., onClick={undefined}).
I suggest you refactor the onClick prop value to return a function
<Button onClick={()=>parentShowFn()}>Edit</Button>
I'm calling a parent method from child component using props and I'm getting this error:
The way I'm passing props to the AddGuest child component is like this:
import React from 'react';
import globalService from '../services/globalService';
import '../styles/chairqueue.css';
import {buttonText,endPoint} from '../constants/global.constants';
import Modal from 'react-bootstrap/Modal'
import ModalDialog from 'react-bootstrap/ModalDialog'
import ModalHeader from 'react-bootstrap/ModalHeader'
import ModalTitle from 'react-bootstrap/ModalTitle'
import ModalBody from 'react-bootstrap/ModalBody'
import ModalFooter from 'react-bootstrap/ModalFooter'
import Button from 'react-bootstrap/Button'
import { useState, useEffect } from 'react';
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import AddGuest from './addGuest'
class CreateMeeting extends React.Component {
constructor(props){
super(props)
this.state={
guestModalShow:false
}
}
as=(a)=>{
console.log('saasa')
this.setState({guestModalShow:a});
}
asd=(a)=>{
console.log(a) // works perfectly
}
render(){
return (
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header >
<label >Cancel</label>
<Modal.Title id="contained-modal-title-vcenter">
New Meeting
</Modal.Title>
<label>Create</label>
</Modal.Header>
<Modal.Body>
<h4><input type="text" className="form-control" placeholder="Meeting title"/></h4>
{/* <DatePicker className="form-control"
selected={startDate}
onChange={setStartDate}
/> */}
<label variant="primary" onClick={()=>this.as(true)}>
Add Guest
</label>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.onHide}>Close</Button>
</Modal.Footer>
<AddGuest
show={this.state.guestModalShow}
savehere={(a)=>this.asd(a)}
onHide={() => this.as(false)}
/>
</Modal>
)
}
}
export default CreateMeeting;
My child component is implemented as:
import React from 'react';
import '../styles/chairqueue.css';
import {buttonText,endPoint} from '../constants/global.constants';
import Modal from 'react-bootstrap/Modal'
import ModalDialog from 'react-bootstrap/ModalDialog'
import ModalHeader from 'react-bootstrap/ModalHeader'
import ModalTitle from 'react-bootstrap/ModalTitle'
import ModalBody from 'react-bootstrap/ModalBody'
import ModalFooter from 'react-bootstrap/ModalFooter'
import Button from 'react-bootstrap/Button'
import { useState, useEffect } from 'react';
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
class AddGuest extends React.Component {
constructor(props){
super(props)
this.state={
startDate:new Date(),
formControls: {
email: '',
name: ''
},
}
}
changeHandler = event => {
const name = event.target.name;
const value = event.target.value;
this.setState({
formControls: {
...this.state.formControls,
[name]:
value
}
});
}
sendData = () => {
console.log('hhhh--')
this.props.savehere("Hey Popsie, How’s it going?");
}
render(){
return (
<Modal {...this.props} >
<Modal.Header closeButton>
<Modal.Title>Add Guest</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4><input type="text" name="name" value={this.state.formControls.name}
onChange={this.changeHandler} required className="form-control" placeholder="Guest Name"/></h4>
<h4><input type="text" className="form-control" name="email" value={this.state.formControls.email}
onChange={this.changeHandler} required placeholder="Guest Email"/></h4>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.props.onHide}>
Close
</Button>
<Button variant="primary" onClick={()=>this.sendData()}>
Save
</Button>
</Modal.Footer>
</Modal>
);
}
}
export default AddGuest;
Im using react boostrap modals and calling another modal. What could be problem causing this error?
The problem here is that a non-stardard input prop savehere of your <AddGuest/> component which is being directly spread into the <Modal/> component when AddGuest is rendered:
render(){
return (
<Modal {...this.props} > {/*<-- This is problematic, as all props
of AddGuest are spread into Modal
including those not supported by
Modal such as "savehere"*/}
...
</Modal>)
}
Rather than spread the props directly to Modal, consider only applying the props that the Modal component supports. A simple solution for your case would be to explictly specify the show and onHide props passed to AddGuest:
render(){
return (
<Modal show={this.props.show} onHide={this.props.onHide}>
...
</Modal>)
}
Hope that helps!
I also met this problem and solved it with Object Destructuring
const {savehere, ...others} = props
return (
<Modal ...others/>
)
use variable savehere to store the callback function, and use variable other to store the propertys which will be passed to <Modal/>
In general this is caused by accidentally adding a non-dom property to a dom element (in this case a div).
If you checkout the spec for div, I suspect that you will not find 'savehere' defined. As such, 'savehere' will have no effect on the div element.
You can see what attributes you can pass to such elements via the mdn pages (linked below but you can just go there and search for them).
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes
As such, it could be argued that this is a mistake on the part of react bootstrap's Modal component which appears to be passing on all props to ModalDialog and somewhere down the line that passes your prop to a .
On you side you can 'fix' this by only passing the props that you wish to give and by reformulating your approach.
The general, and quite clean, practice that seems to be ubiquitous in the React world (pushed by linter defaults, used in example code in docs and guides etc) would be to:
refactor this quite simple set of components into functions
destructure your props at the component definition
pass only the variables you need
keep your handlers in the parent
So the parent component would look sth like:
const CreateMeeting = ({modalProp1, modalProps2, whatever}) => {
const [guestModalShow, setGuestModalShow] = useState(false)
const handleSaveClick = () => {
console.log('clicked')
}
const closeModal = () => setGuestModalShow(false)
return (
<Modal
modalProp1={modalProp1}
modalProps2={modalProp2}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header >
<label >Cancel</label>
<Modal.Title id="contained-modal-title-vcenter">
New Meeting
</Modal.Title>
<label>Create</label>
</Modal.Header>
<Modal.Body>
<label variant="primary" onClick={()=>this.as(true)}>
Add Guest
</label>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.onHide}>Close</Button>
</Modal.Footer>
<AddGuest
show={guestModalShow}
savehere={handleSaveClick}
onHide={closeModal}
/>
</Modal>
)
}
}```
for react-bootstrap you might still spread out the props ( i'm using functional components, React 18.1.0 and typescript)
<CustomModal handleshow={setCustomModalShow}/> <-- from the parentcomponent with customprop (here a ReactSetAction from usestate())
custom Modal component:
CustomModal = (props:
JSX.IntrinsicAttributes &
Omit<Pick<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>,
HTMLDivElement>, "key" | keyof HTMLAttributes<HTMLDivElement>> &
{ ref?: ((instance: HTMLDivElement | null) => void) |
RefObject<HTMLDivElement> | null | undefined; },
BsPrefixProps<"div"> & ModalProps> &
BsPrefixProps<"div"> &
ModalProps &
{ children?: ReactNode; }, //all the types from a React-Bootstrap-Modal
handleshow: (e: boolean) => void // followed by the customprop and its type
) => {
return(
<Modal {...{ ...props, handleshow: handleshow }} >
...modalstuff here
<Modal/>)
}
and the error from the OP went away (it worked as intended before, yet the error popped up).
this.asd this is a function, you don’t need to wrap it in the function again.
<AddGuest
show={this.state.guestModalShow}
savehere={this.asd}
onHide={() => this.as(false)}
/>
New web dev I found some nice pure HTML/JQuery template.
https://www.creative-tim.com/
I have to do an app with React and I would like to implement to login modal template on this web site.
https://www.creative-tim.com/product/login-and-register-modal
I'm not sure about the approch I have to make to be able to convert this to React.
I have to handle the onClick on the buttons and make the modal appear.
How can I load the component Modal by change the CSS like the way they do with JQuery ?
import React, { Component } from 'react';
import { Grid, Row, Col, Button } from 'react-bootstrap/lib'
import './Login.css'
import LoginModal from '../LoginModal/LoginModal'
class Login extends Component {
openLoginModal(){
console.log('openLoginModal');
// showLoginForm();
}
openRegisterModal(){
console.log('openRegisterModal');
// showRegisterForm();
}
render() {
return (
<Grid>
<Row>
<Col sm={4}></Col>
<Col sm={4}>
<Button bsClass="btn big-login" data-toggle="modal" onClick={this.openLoginModal}>Log In</Button>
<Button bsClass="btn big-register" data-toggle="modal" onClick={this.openRegisterModal}>Register</Button>
</Col>
<Col sm={4}></Col>
</Row>
<LoginModal />
</Grid>
)
}
}
export default Login
In React you don't need to use CSS to show and hide the modal. You can just use an inline conditional expression with JSX.
first you need to build a RegisterModal...
...Then
import React, { Component } from 'react';
import { Grid, Row, Col, Button } from 'react-bootstrap/lib'
import './Login.css'
import LoginModal from '../LoginModal/LoginModal'
import RegisterModal from '../RegisterModal/RegisterModal';
class Login extends Component {
constructor() {
super();
// create state properties to record open/close for each modal
this.state = {
loginOpen: false,
registerOpen: false
};
}
// toggle your state
openLoginModal(){
this.setState({ loginOpen: true, registerOpen: false });
}
openRegisterModal(){
this.setState({ loginOpen: false, registerOpen: true });
}
render() {
// `{ true && <div /> }` will render div if true
return (
<Grid>
<Row>
<Col sm={4}></Col>
<Col sm={4}>
<Button bsClass="btn big-login" data-toggle="modal" onClick={this.openLoginModal}>Log In</Button>
<Button bsClass="btn big-register" data-toggle="modal" onClick={this.openRegisterModal}>Register</Button>
</Col>
<Col sm={4}></Col>
</Row>
{ this.state.loginOpen &&
<LoginModal />
}
{ this.state.registerOpen &&
<RegisterModal />
}
</Grid>
)
}
}
Hi I'm implementing a sample app with react,I'm also using youtube api search and react bootstrap.
The idea is the following, I enter a search term, for example "PS4" this will call youtube api search and will bring back 5 matching results in a json format.
With this json, I render a list of thumbnails with videos, title, description,(similar to youtube website). if I click on one of the thumbnails, the idea is to display a modal dialog with the video inside. Here's the problem, it's always loading the same video.
I omit the imports at the beginning of the file
VideoList.js
class VideoList extends React.Component {
constructor(props) {
super(props);
this.openModal = this.openModal.bind(this);
this.close = this.close.bind(this);
this.state = {
showModal: false
}
}
openModal(video) {
this.setState({ showModal: true });
}
close() {
this.setState({ showModal: false });
}
render() {
const videoItems = this.props.videos.map(video =>
<Col xs={6} md={4}>
<Thumbnail onClick={() => this.openModal(video)}
className="thumbnail"
src={video.snippet.thumbnails.default.url}
alt={video.snippet.description}
key={uid()}
>
<h3>{video.title}</h3>
<p>{video.snippet.description}</p>
<hr />
<i>Date: {video.snippet.publishedAt}</i>
<VideoView show={this.state.showModal} video={video} close={this.close} />
</Thumbnail>
</Col>
);
return (
<Grid>
<Row>
{videoItems}
</Row>
</Grid>
);
}
}
export default VideoList;
VideoView.js
const VideoView = props => {
if (!props) {
return <div>{'Could not load the video'}</div>
}
return (
<div className="static-modal">
<Modal show={props.show}>
<Modal.Header>
<Modal.Title>{props.video.snippet.title}</Modal.Title>
</Modal.Header>
<Modal.Body>
One fine body...
</Modal.Body>
<Modal.Footer>
<Button onClick={() => props.close()}>Close</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default VideoView;
If I click on one of the thumbnails, a modal popup shows with the video (some information of it, not the video itself), the thing is that it always shows the last video (the last in the list of thumbnails)
Another thing: I'm facing a design problem here because I think I should delegate the onclose action to the modal dialog, in this case, the VideoView however the videoView component is a stateless component, I'm only sending some props to render it, (check the onclose callback). Is this a correct approach? Thanks!
You create multiple VideoView elements sharing the same prop this.state.showModal. It means that when this.state.showModal is true then you show all modals at once, and you see only one of them which is on top. Create one VideoView element and pass it a video to show. Also to show/hide modal you can check if this.state.video is not equal to null - if it have value assigned then modal should be visible.
class VideoList extends React.Component {
constructor(props) {
super(props);
this.openModal = this.openModal.bind(this);
this.close = this.close.bind(this);
this.state = {
video: null
}
}
openModal(video) {
this.setState({ video: video });
}
close() {
this.setState({ video: null });
}
render() {
const videoItems = this.props.videos.map(video =>
<Col xs={6} md={4}>
<Thumbnail onClick={() => this.openModal(video)}
className="thumbnail"
src={video.snippet.thumbnails.default.url}
alt={video.snippet.description}
key={uid()}
>
<h3>{video.title}</h3>
<p>{video.snippet.description}</p>
<hr />
<i>Date: {video.snippet.publishedAt}</i>
</Thumbnail>
</Col>
);
return (
<div>
<Grid>
<Row>
{videoItems}
</Row>
</Grid>
<VideoView show={this.state.video !== null} video={this.state.video} close={this.close} />
</div>
);
}
}
export default VideoList;