Reactstrap: Getting Modal to work - javascript

Trying to learn React and Reactstrap and trying to get Modals to work. When I click the button, it should toggle the Modal, but right now it's giving me this error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of 'App'
Can't figure out what I'm doing wrong. Can someone help let me know where I'm going wrong here? Appreciate any help you can give.
I'd like to use the most recent versions of React and Reactstrap if possible.
Here's a link on Codepen: https://codepen.io/lieberscott/pen/ddYNVP
const { Button,
Container,
Modal,
ModalTitle,
ModalHeader,
ModalBody,
ModalFooter } = Reactstrap;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showModal: false
}
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal() {
console.log("hello");
this.setState({
showModal: !this.state.showModal
})
}
render() {
return (
<Container>
<Headline />
<Box />
<Button outline color="primary" onClick={this.toggleModal}>Click</Button>
<Modal isOpen={this.state.showModal} toggle={this.toggleModal} className="modal">
<ModalHeader>
<ModalTitle id="modalTitle">
Add a Recipe
</ModalTitle>
</ModalHeader>
<ModalBody>
Modal body
</ModalBody>
<ModalFooter>
Modal footer
</ModalFooter>
</Modal>
</Container>
);
}
}
class Headline extends React.Component {
render() {
return (
<div>
Recipes
</div>
);
}
}
class Box extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [
{
name: "Tofu tacos",
ingredients: [
"Shells",
"lettuce",
"tofu",
"paprika"
]
},
{
name: "Spaghetti",
ingredients: [
"pasta",
"sauce",
"salt"
]
}
] // end of items
} // end of this.state
} // end of constructor
render() {
const allitems = this.state.items.map(item => {
return (
<div>
{item.name}
</div>
);
})
return (
<div>
{allitems}
</div>
);
}
}
const app = document.getElementById("app");
ReactDOM.render(<App />, app);

I don't know how to share my code via codepen like you did, sorry.
I did these changes:
toggleModal() {
console.log("hello");
console.log( 'before setState: ', this.state );
this.setState({
showModal: !this.state.showModal
})
console.log( 'after setState: ', this.state );
}
The Button onClick event, when you do onClik={this.something} this this refers to the Button element. I changed to:
<Button outline color="primary" onClick={() => this.toggleModal()}>Click</Button>
When I do onClick={ () => this.something() } allows me to make use of the methods of my class.
Just look at the console.log output and you'll see clicking on button change showModal to true or false.
Take a look on cephalization's answer: https://forum.freecodecamp.org/t/difference-in-reactjs-onclick-function-binding/116027/2

It is far easier this way
add your state in the constructor example
this.state={showLogOut: false}
in your return all you need is to setState true example
<Button onClick={() => this.setState({showLogOut: true})}>Show modal</Button>
now all we need is the modal code since we have an state and a setState
<Modal isOpen={this.state.showLogOut} fade="false" toggle={() => this.setState({showLogOut: false})}>
<ModalHeader toggle={() => this.setState({showLogOut: false})}>Ready to leave?</ModalHeader>
<ModalBody>
<p>Press logout to end session.</p>
</ModalBody>
<ModalFooter>
<Button onClick={() => this.setState({showLogOut: false})}>Cancel</Button>
<Button color="info">Logout</Button>
</ModalFooter>
this should be enough to show your modal: I am assuming you are using react-strap and that you are importing the required elements but just to make sure here is what you need to import
import { Button, Modal, ModalBody, ModalFooter, ModalHeader} from 'reactstrap';

Reactstrap Modal don't show, because is need fade set on False. plz look my code who worked.
class Demoextends extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
fade: false
};
this.toggle = this.toggle.bind(this);
};
toggle() {
console.log("hello");
this.setState({
modal: !this.state.modal
});
console.log( 'after setState: ', this.state );
}
render() {
return (
<div>
<Button color="danger" onClick={this.toggle}>Launch</Button>
<Modal isOpen={this.state.modal} fade={this.state.fade } toggle={this.toggle}>
<ModalHeader toggle={this.toggle}>Modal title</ModalHeader>
<ModalBody>
</ModalBody>
<ModalFooter>
<Button onClick={this.toggle}>Do Something</Button>{' '}
<Button onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default Demoextends;
Of course You must add and part of reactstrap in your code

Related

Common Delete Modal Component in react with Reactstrap

I am trying to create a common Delete Component and call from other components with reactstrap.
Here is my DeleteModal
class DeleteModal extends 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}>Delete the item</ModalHeader>
<ModalBody>
Do you want to delete the item ?
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.toggle}>yes</Button>
<Button color="secondary" onClick={this.toggle}>No</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default DeleteModal;
When the user click the YES button I need to return the ID of the item. So that I can know that delete confirm has been click.
Here is the other component from where I want to call the Modal
class Home extends Component {
handleDeleteClick(id) {
console.log(id);
}
}
handleDeleteClick is the method to call the modal also I need to pass the ID to the deleteModal component and when the user click on the YES button I need to get back the id from the DeleteModal.
How can I achieve this? I tried to research on this but not able to identify the solution.
I changed a bit the way you were doing, instead of sending the id to modal, you just save the deleteID on the state. It makes the DeleteModal more specific, it does not have to know any ID, it just let you know if you have to delete or not and the container component will manage that.
class DeleteModal extends Component {
constructor(props) {
super(props);
this.state = {
modal: false
};
this.toggle = this.toggle.bind(this);
this.deleteItem = this.deleteItem.bind(this);
this.close = this.close.bind(this);
}
toggle() {
this.setState(prevState => ({
modal: !prevState.modal
}));
}
deleteItem(){
this.toggle()
this.props.clickHandler()
}
close(){
this.toggle()
this.props.onClose()
}
render() {
return (
<div>
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<ModalHeader toggle={this.toggle}>Delete the item</ModalHeader>
<ModalBody>
Do you want to delete the item ?
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.deleteItem}>yes</Button>
<Button color="secondary" onClick={this.close}>No</Button>
</ModalFooter>
</Modal>
</div>
);
}
}
export default DeleteModal;
import DeleteModal from './DeleteModal'
class Home extends Component {
constructor(props) {
super(props);
this.state = {
deleteID: null
};
this.handleDeleteClick = this.handleDeleteClick.bind(this);
this.onCloseModal = this.onCloseModal.bind(this);
this.openModal = this.openModal.bind(this);
}
handleDeleteClick() {
//delete object
this.onCloseModal()
}
onCloseModal(){
this.setState({deleteID: null})
}
openModal(deleteID){
this.setState({deleteID})
}
return(<div><button onClick={()=> this.openModal(id)}>Delete</button>
{deleteID && <DeleteModal clickHandler={this.handleDeleteClick} onClose={this.onCloseModal}/>}
</div>)
}

Object is null in JSX tag

So I'm writing an application with a spring boot backend and react.js frontend. I am having an extremely annoying, and basic problem with react, and I'm not a very experienced JS developer...I'm a Java dev.
render() {
console.log(this.videoAreaData)
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>{libs}</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
I get the following output:
{
"libs": [
{
"name": "videos",
"library": null
}
]
}
So then I add
console.log(this.videoAreaData.libs)
and get this following output
[
{
"name": "videos",
"library": null
}
]
So to test printing the first element I should use this.videoAreaData.libs[0] obviously right? Apparently not
TypeError: this.videoAreaData.libs is undefined[Learn More]
What I want to do is iterate over the array in my JSX code using .map, but the object is literally always undefined! I've tried using setState with a const and all kinds of stuff! The data is passed from my parent app.js class which uses this:
componentDidMount() {
this.setState({isLoading: true})
libraryAccessor.listLibraries().then(libs => {
this.setState({ isLoading: false, show: false, libraries: libs })
})
}
libs is then pasted as a parameter into my code using a property like this
<LibraryLister libs={libraries} ref="libraries"></LibraryLister>
then it goes to the constructor of LibraryLister here
videoAreaData
constructor(videoAreaData) {
super(videoAreaData)
this.videoAreaData = videoAreaData
}
Now I assume all of that is done correctly, as it's non-null in my render method. If I put console.log(videoAreaData) within the JSX tags in a {} it's not null either, so it's definitely not supposed to be!
Here is what I finally want to do:
<Modal.Body>
{(this.videoAreaData.libs.map((library)=> {
return <p className="libname"> library.name </p>
}))}
</Modal.Body>
I feel like I'm doing something very very wrong here. That being said I have another project using the exact same stack, but made in typescript, and it works fine doing almost exactly this. Typescript is super irritating to use though, so I'd uh...prefer not to. Thanks in advance for any help
EDIT: Full code https://pastebin.com/daipM2gY
Also this alternate version of my render method prints the data as expected, so it...should not be undefined
render() {
console.log("PRINTING VID DATA: "+this.videoAreaData)
console.log(this.videoAreaData)
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>
{console.log("PRINTIN BODY")}
{console.log(this.videoAreaData)}
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
Output:
PRINTIN BODY
{
"libs": [
{
"name": "videos",
"library": null
}
]
}
So I uh figured out what I was doing wrong actually. Nothing to do with the state (the isLoading stuff on componentLoad worked fine as it did in another project) but the POJO I was sending.
So here's what happened: this.state.libraries[x] was in fact undefined, because this.state.libraries was actually an object. Containing another object called libraries which was the array from my POJO.
My POJO I was sending had "libraries" as the root object with an array inside of it. It was then being packed into the state as libraries, and so I had to use this.state.libraries.libraries[x]
Really stupid problem, but glad it's solved.
Thanks for your help guys!
Final code for those interested:
import React from 'react';
import { Modal, Button } from 'react-bootstrap';
import LibraryAccessor from '../accessors/LibraryAccessor'
import './navigator.css'
var libraryAccessor = new LibraryAccessor()
var librarylist = []
export default class LibraryLister extends React.Component {
state = { loadModal: false, libs: [] }
constructor(props) {
super(props)
this.state = {
...this.state,
libs: props
}
}
render() {
console.log(this.state.libs.libs[0])
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>
{this.state.libs.libraries.map(item=> {
console.log("ITEM")
})}
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
close = () => {
this.setState({ ...this.state, show: false });
}
open = () => {
this.setState({ ...this.state, show: true });
}
}
You have to study react
React’s props don’t work your post.
First you have to edit your videoAreaData constructor.
constructor(props) {
super(props)
this.videoAreaData = this.props.videoAreaData;
}
I recommend to read props in http://reactjs.org/docs
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { Button, Popover, Tooltip } from 'react-bootstrap';
// My components
import Navigator from './components/Navigator';
import LibraryLister from './components/LibraryLister';
import LibraryAccessor from './accessors/LibraryAccessor';
var loadingChildren = {};
var libraryAccessor = new LibraryAccessor();
var jsAccessor = new LibraryAccessor();
export default class App extends Component {
// React Component constructor parameter is `props`
constructor(props) {
super(props);
this.state = {
isLoading: true
}
}
open = () => {
this.refs.navigator.open()
}
openLibs = () => {
this.refs.libraries.open();
}
setLoading(refName, value) {
loadingChildren[refName].delete(refName);
if (loadingChildren.size < 1) {
// this.state = { isLoading: value }
// if you change state, you have to use setState; it's very very important.
// setState function is asynchronous because it relate performance.
this.setState({
isLoading: value
})
}
}
render() {
const { isLoading, libraries } = this.state;
console.log(libraries) // check please~!
if(isLoading) return <pre>Loading...</pre>
return (
<div className="App">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to Your Server</h1>
</header>
<Button onClick={this.open}>Show Navigator</Button>
<Button onClick={this.openLibs}>Show Libraries</Button>
<Navigator ref="navigator"></Navigator>
<LibraryLister libs={libraries} ref="libraries"></LibraryLister>
</div>
);
}
getLibraries = (libraries) => {
this.setState({
isLoading: false,
libraries
});
}
componentDidMount() {
libraryAccessor
.listLibraries()
.then(this.getLibraries);
}
}
import React, { Component } from 'react';
import { Modal, Button } from 'react-bootstrap';
import LibraryAccessor from '../accessors/LibraryAccessor';
import './navigator.css';
var libraryAccessor = new LibraryAccessor();
var librarylist = [];
export default class LibraryLister extends Component {
constructor(props) {
super(props);
this.state = {
loadModal: false
};
this.videoAreaData = props.videoAreaData
};
render() {
console.log("PRINTING VID DATA: "+this.videoAreaData)
console.log(this.videoAreaData)
return (
<div className='lib-modal'>
<Modal show={this.state.show} onHide={this.close}>
<Modal.Header closeButton>
<Modal.Title>List of Available Libraries</Modal.Title>
</Modal.Header>
<Modal.Body>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
close = () => {
this.setState({ ...this.state, show: false });
}
open = () => {
this.setState({ ...this.state, show: true });
}
}

Reactstrap dropdownitem not triggering setState

What is happening?
Hello, I'm just using reactstrap and react js for a bit and i have some issues regarding the component. I want to trigger show modal when i click on the component inside the component. this is the module that i've been working for a while:
What should be happening?
I expect it to trigger setState when i click the div inside DropdownItem
// Base Account placeholder component <PARENT>
export class Account extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
dropDownOpen : false
};
}
toggle() {
this.setState({
dropDownOpen : !this.state.dropDownOpen
});
}
render() {
return (
<Dropdown isOpen={this.state.dropDownOpen} toggle={this.toggle}>
<DropdownToggle nav caret>
Account
</DropdownToggle>
<DropdownMenu>
<DropdownItem>
<div onClick={() => console.log("fire pew pew")}>
<AccountSettingModal />
</div>
</DropdownItem>
<DropdownItem>
Pricings
</DropdownItem>
<DropdownItem divider />
<DropdownItem>
Logout
</DropdownItem>
</DropdownMenu>
</Dropdown>
);
}
}
this is the modal that i want to show:
// Base account placeholder for Modal Setting
class AccountSettingModal extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
modal: !this.state.modal
});
// console.log("modalSetting: " + this.state.modalSetting);
}
render() {
return (
****************! trigger this when i click the dropdownitem !*********************
<div onClick={this.toggle}>
<a>Setting</a>
<Modal isOpen={this.state.modal} toggle={this.toggle} className="account-setting-box">
<ModalHeader toggle={this.toggle}>Setting</ModalHeader>
<ModalBody className="scroll">
</ModalBody>
<ModalFooter className="modal-footer">
</ModalFooter>
</Modal>
</div>
);
}
}
help much appreciated because i've been dealing with this issues for about two days. I can use custommenu for solving this problem, but in the name of my learning process, I'm convinced that i need to ask about this issue that i got.
Thank you very much! 👍
Change the name of the toggle function in the modal as you are using same name function toggle() in parent and child component. Rename toggle() in parent or child component and it will be good to go.
// Account
toggleDropdown() {
this.setState({
dropDownOpen : !this.state.dropDownOpen
});
}
and/or also change:
// AccountSettingModal
toggleState() {
this.setState({
modal: !this.state.modal
});
}

Sharing state from parent to child component in React

I am having an issue binding the state of a parent component to the state of a child. A look at the code:
Parent Component:
class ParentForm extends React.Component {
constructor(){
super();
this.state = {
showDialog: false
};
}
toggleDialog() {
this.setState({showDialog: !this.state.showDialog});
}
return (
<div >
<Button color='primary' onClick={() => this.toggleDialog()}></Button>
<MyDialog open={this.state.showDialog}/>
</div>
);
}
Child Component:
export default class MyDialog extends Component {
constructor(props){
super(props);
this.state = {
open: this.props.open
};
}
handleRequestClose = () => {
this.setState({ open: false });
};
render() {
return (
<div>
<Dialog
fullScreen
open={this.state.open}
onRequestClose={() => this.handleRequestClose()}
transition={<Slide direction="up" />}
>
<DialogTitle>{'Title'}</DialogTitle>
<DialogContent>
<DialogContentText>
This is my dialog
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => this.handleRequestClose()} color="primary">Close</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
In the Parent Component, if I make the state.showDialog property true, the dialog will open when the page loads. But once I close it one time, I am never able to open it again. If I have it set to false, it doesn't load when the page loads, and I am never able to open the dialog, even when I click the button on the parent component. Thank you in advance for taking your time to help.
Since you are setting the local state based on the parent, you need to make use of componentWillReceiveProps before v16.3.0 or getDerivedStateFromProps/memoization/key modification thereafter, since your state is set only on the first time and never thereafter. However you don't even need a local state in MyDialog component, you can just make use of Props and communicate from child to the parent component.
Parent
class ParentForm extends React.Component {
constructor(){
super();
this.state = {
showDialog: false
};
}
toggleDialog() {
this.setState({showDialog: !this.state.showDialog});
}
closeDialog() {
this.setState({showDialog: false})
}
return (
<div >
<Button color='primary' onClick={ this.toggleDialog}></Button>
<MyDialog open={this.state.showDialog} closeDialog={this.closeDialog}/>
</div>
);
}
MyDialog (child)
export default class MyDialog extends Component {
constructor(props){
super(props);
}
render() {
return (
<div>
<Dialog fullScreen open={this.props.open} onRequestClose={this.props.closeDialog} transition={<Slide direction="up" />}>
<DialogTitle>{'Title'}</DialogTitle>
<DialogContent>
<DialogContentText>
This is my dialog
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.props.closeDialog} color="primary">Close</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
handleRequestClose method should be like this.
handleRequestClose = () => {
this.setState({ open: this.props.open});
};
Edit 1.
You also need to update the parent state when you close the dialog.
toggleDialog(val) {
if(val){
this.setState({showDialog: val});
}else {
this.setState({showDialog: !this.state.showDialog});
}
}
return (
<div >
<Button color='primary' onClick={() => this.toggleDialog()}></Button>
<MyDialog toggleDialog = {this.toggleDialog} open={this.state.showDialog}/>
</div>
);
And,
componentWillRecieveProps(nextProps) {//Lifecycle method to get the updated props
this.setState({ open: nextProps.open });
}
handleRequestClose = () => {
this.setState({ open: !this.state.open},()=>{
this.props.toggleDialog (this.state.open);
});
};
Your child component currently only receives the parent's showDialog prop value once, only when it's initiated in the constructor.
You need to use componentWillRecieveProps and setState of the child's component with the updated value.
So:
componentWillRecieveProps(nextProps) {
this.setState({ open: nextProps.open });
}
EDIT: Need to use nextProps, not this.props
class ParentForm extends React.Component {
constructor(){
super();
this.state = {
showDialog: false
};
}
toggleDialog() {
this.setState({showDialog: !this.state.showDialog});
}
closeDialog() {
this.setState({showDialog: false});
}
return (
<div >
<Button color='primary' onClick={() => this.toggleDialog()}></Button>
<MyDialog open={this.state.showDialog} closeDialog={() => this.closeDialog()/>
</div>
);
}
export default class MyDialog extends Component {
handleRequestClose = () => {
this.props.closeDialog();
};
render() {
return (
<div>
<Dialog
fullScreen
open={this.state.open}
onRequestClose={() => this.handleRequestClose()}
transition={<Slide direction="up" />}
>
<DialogTitle>{'Title'}</DialogTitle>
<DialogContent>
<DialogContentText>
This is my dialog
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => this.handleRequestClose()} color="primary">Close</Button>
</DialogActions>
</Dialog>
</div>
);
}
}

Creating Modal Component with Separate Modal Trigger Button

I'm new to React so bear with me.
I'm trying to create a modal component that will be triggered from a onClick() function from any global element i.e: link, button, span or whatever throughout the whole app.
import React from 'react';
import ReactDOM from 'react-dom';
const display = {
display: 'block'
};
const hide = {
display: 'none'
};
class Modal extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
toggle: false
}
}
toggle(event) {
this.setState(prevState => ({
toggle: !prevState.toggle
}));
}
render() {
return (
<div>
<button onClick={this.toggle}>Show Modal</button>
<div className="modal" style={this.state.toggle ? display : hide} >
<div className="modal-content">
{this.props.children}
<button onClick={this.toggle}>Close</button>
</div>
</div>
</div>
);
}
}
module.exports = Modal;
You can use react-bootstrap-modal:
npm install --save react-bootstrap-modal
Then in your component:
import React from 'react';
import Modal from 'react-bootstrap-modal';
export default class ModalExample extends React.Component {
constructor(props){
super(props);
this.state = {
open: false,
}
}
openModal = () => this.setState({ open: true })
closeModal = () => this.setState({ open: false })
render(){
return (
<div>
<button type='button' onClick={this.openModal}>Launch modal</button>
<Modal
show={this.state.open}
onHide={this.closeModal}
aria-labelledby="ModalHeader"
>
<Modal.Header>
<Modal.Title id='ModalHeader'>A Title Goes here</Modal.Title>
<div onClick={this.closeModal}>CLOSE HERE</div>
</Modal.Header>
<Modal.Body>
<p>Some Content here</p>
</Modal.Body>
<Modal.Footer>
// If you don't have anything fancy to do you can use
// the convenient `Dismiss` component, it will
// trigger `onHide` when clicked
<Modal.Dismiss className='btn btn-default'>Cancel</Modal.Dismiss>
// Or you can create your own dismiss buttons
<button className='btn btn-primary' onClick={this.closeModal}>
CLOSE HERE TOO
</button>
</Modal.Footer>
</Modal>
</div>
)
}
}
For further reference, please go here:
https://github.com/jquense/react-bootstrap-modal
You may also need to include bootstrap-CSS file if necessary! Please post here some errors if any, thanks

Categories