I've hit a wall and have no clue what to do. I'm simply trying to close my bootstrap modal but it just won't close. It opens fine but again it just won't close. I've tried so many ways to rectify this that there won't be enough space on this post to list all my attempts.
What am I doing wrong and how can I fix this?
Here's App.js:
import React, { Component } from 'react';
import Product from './Product';
import { ProductConsumer } from "../context";
import TitleBody from "./TitleBody";
import AboutButton from "./AboutButton";
import AboutButtonModal from "./AboutButtonModal";
export default class App extends Component {
constructor(props) {
console.log("Props - ", props);
super(props);
this.state = {
modalVisible: false
};
this.openModal = this.openModal.bind(this);
}
openModal() {
console.log("Open modal called ", this.state.modalVisible);
const modalVisible = !this.state.modalVisible;
this.setState({
modalVisible
}, console.log(this.state.modalVisible));
}
render() {
let styles = this.state.modalVisible
? { display: "block" }
: { display: "none" };
return (
<React.Fragment>
<div className="py-5">
<div className="container">
<TitleBody name="Welcome to" title="Cruskip"/>
<AboutButtonModal show={this.state.modalVisible} onClick={this.openModal} style={styles}/>
<AboutButton onClick={this.openModal}/>
</div>
</div>
</div>
</React.Fragment>
);
}
}
Here's AboutButtonModal.js:
import React from 'react';
import './AboutButtonModal.scss';
const Modal = ({ handleClose, show, children}, props) => {
const showHideClassname = show ? "modal display-block" : "modal display-none";
return(
<div className={showHideClassname} style={props.style}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
onClick={props.onClick}
className="close"
>
×
</button>
<h4 className="modal-title">Modal Header</h4>
</div>
<div className="modal-body">
<p>Some text in the modal.</p>
</div>
<div className="modal-footer">
<button
onClick={props.onClick}
type="button"
className="btn btn-default"
>
Close
</button>
</div>
</div>
</div>
</div>
);
};
export default Modal;
Here's AboutButton.js:
import React from 'react';
import './AboutButton.scss';
const AboutButton = (props) => {
return(
<div className="row">
<button className="about-btn" type="button" onClick={props.onClick}>
About Us
</button>
</div>
);
};
export default AboutButton;
If you are using ({ handleClose, show, children }, props),It won't give you to access of props onClick function..
So to use props, just apply object destructing of props..Now,Your code will become like this...
const Modal = ({ handleClose, show, children,...props})
Related
I want to return a component <SaveTask/> to a div tag where <div id="saveTask">. I tried to use this.setState(), but it changes the container and displays the <SaveTask/> component. I want to keep that container and under that container, I want to append a component after clicking a button. Is there any way to add component using document.getElementById('saveTask').
import React, { Component } from 'react'
import SaveTask from './SaveTask';
export default class TaskHeader extends Component {
constructor(props) {
super(props)
this.state = {
saveTask: false,
}
this.showComp = () => {
this.setState({
saveTask: true,
})
}
}
render() {
if(this.state.saveTask) {
return (
document.getElementById('saveTask').innerHTML = <SaveTask/>
)
}
return (
<div>
<div className="container-fluid bg-white border-bottom">
<div className="row">
<div className="col-12">
<div className="tasks-upper text-muted">TASK MANAGEMENT APP</div>
<div className="tasks-lower">
<span className="text-secondary text-size">TASK</span><button className="text-primary btn-size" type="button" onClick={this.showComp}>+</button>
</div>
</div>
</div>
</div>
<div id="saveTask">
Return a component to this section when clicking the button
</div>
</div>
)
}
}
Yeah, so the beauty of react is that you don't need to and shouldn't be updating the innerhtml to update a component. You can simply use a ternary to determine what component to show later on:
import React, { Component } from 'react'
import SaveTask from './SaveTask';
export default class TaskHeader extends Component {
constructor(props) {
super(props)
this.state = {
saveTask: false,
}
this.showComp = () => {
this.setState({
saveTask: true,
})
}
}
render() {
return (
<div>
<div className="container-fluid bg-white border-bottom">
<div className="row">
<div className="col-12">
<div className="tasks-upper text-muted">TASK MANAGEMENT APP</div>
<div className="tasks-lower">
<span className="text-secondary text-size">TASK</span><button className="text-primary btn-size" type="button" onClick={this.showComp}>+</button>
</div>
</div>
</div>
</div>
{this.state.saveTask ? (
<SaveTask/>
) : (
<div id="saveTask">
Return a component to this section when clicking the button
</div>
)}
</div>
)
}
}
In this case you're saying, "if this.state.saveTask is true, then show <SaveTask /> otherwise, show the <div id="saveTask"> element. If you wanted inside that div, then you would just move the ternary inside it.
I hope following answer will help you
Achive using Functional Component
import React, { useState } from "react";
import SaveTask from "./SaveTask";
function TaskHeader() {
const [show, setShow] = useState(false);
const showComp = () => {
setShow(true);
};
return (
<div>
<div className="container-fluid bg-white border-bottom">
<div className="row">
<div className="col-12">
<div className="tasks-upper text-muted">TASK MANAGEMENT APP</div>
<div className="tasks-lower">
<span className="text-secondary text-size">TASK</span>
<button
className="text-primary btn-size"
type="button"
onClick={showComp}
>
+
</button>
</div>
</div>
</div>
</div>
<div id="saveTask">{show && <SaveTask />}</div>
</div>
);
}
export default TaskHeader;
Achive using Class Component
import React, { Component } from 'react'
import SaveTask from './SaveTask';
export default class TaskHeader extends Component {
constructor(props) {
super(props)
this.state = {
saveTask: false,
}
this.showComp = () => {
this.setState({
saveTask: true,
})
}
}
render() {
return (
<div>
<div className="container-fluid bg-white border-bottom">
<div className="row">
<div className="col-12">
<div className="tasks-upper text-muted">TASK MANAGEMENT APP</div>
<div className="tasks-lower">
<span className="text-secondary text-size">TASK</span><button className="text-primary btn-size" type="button" onClick={this.showComp}>+</button>
</div>
</div>
</div>
</div>
<div id="saveTask">`
{ this.state.saveTask && <SaveTask/> }
</div>
</div>
)
}
}
I decided to use the react-draggable npm package for my modal window using ant-design, (for those who don't know react-draggable, this is a simple component for making elements draggable). But I am having difficulty, I tried to wrap my modal window inside but I got an error.
Cannot read property 'className' of undefined
import React, { useState } from "react";
import "antd/dist/antd.css";
import Modal from "antd/es/modal/Modal";
import Draggable from "react-draggable";
import Button from "antd/es/button";
const App = (props) => {
const [visible, setVisible] = useState(false);
return (
<Draggable>
<Button onClick={() => setVisible(true)} type="primary">
Themes
</Button>
<Modal
title="Customize the theme to your liking"
centered
visible={visible}
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
width={700}
>
<div className={"SideBarModal_Wrapper"}>
<div className={"SideBarModal_Appearance"}>
<div className={"SideBarModal_Child_Appearance"}>
<p>Appearance</p>
</div>
<div>{props.SideBarWallpaperList}</div>
</div>
<div className={"SideBarModal_Accept_Color"}>
<div className={"SideBarModal_Child_Color"}>
<p>Colors</p>
</div>
<div>{props.list}</div>
</div>
</div>
</Modal>
</Draggable>
);
};
export default App;
You can see my code in codesandbox there I have already installed all the required packages, but I could not apply react-draggable. Here is the link https://codesandbox.io/s/xenodochial-mendel-4bdun
I believe you need to have a single child element in Draggable.
So either put only the Button in the Draggable or wrap your Button and Modal in a div.
updated: https://codesandbox.io/s/currying-river-olme0?file=/src/App.js
The documentation says it loud and clear, Only a single child is allowed or an Error will be thrown. :)
Bind the child components as one single child. (e.g, using a div as I've done below)
import React, { useState } from "react";
import "antd/dist/antd.css";
import Modal from "antd/es/modal/Modal";
import Draggable from "react-draggable";
import Button from "antd/es/button";
const App = (props) => {
const [visible, setVisible] = useState(false);
return (
<Draggable>
<div>
<Button onClick={() => setVisible(true)} type="primary">
Themes
</Button>
<Modal
title="Customize the theme to your liking"
centered
visible={visible}
onOk={() => setVisible(false)}
onCancel={() => setVisible(false)}
width={700}
>
<div className={"SideBarModal_Wrapper"}>
<div className={"SideBarModal_Appearance"}>
<div className={"SideBarModal_Child_Appearance"}>
<p>Appearance</p>
</div>
<div>{props.SideBarWallpaperList}</div>
</div>
<div className={"SideBarModal_Accept_Color"}>
<div className={"SideBarModal_Child_Color"}>
<p>Colors</p>
</div>
<div>{props.list}</div>
</div>
</div>
</Modal>
</div>
</Draggable>
);
};
export default App;
The basic concept is actually like this :
import Draggable from 'react-draggable';
<div className="modal fade show d-block" id="myModal">
<div className="modal-dialog">
<Draggable>
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">Modal Heading</h4>
<button type="button" className="close" data-dismiss="modal">×</button>
</div>
<div className="modal-body">
<h4>This is body</h4>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</div>
</Draggable>
</div>
</div>
Try to figure out how to change it if you use react-bootstrap
I am trying to achieve a modal popup(which is stored in postview class) to appear when a onclick is called in any post on postcontainer class. I am new to react, so I would love your suggestion on improving code.
Class 1 (PostCointainer)
This is main class which shows multiple post from an array. I want the modal to appear from class 2 when any post is clicked
import React, { Component, useState } from 'react';
import '../App.css';
import PostView from './PostView';
function RenderPost({posts}) {
return (
<div className="post-holder shadow-sm p-3 mb-4 bg-white rounded" key={posts.id} onClick={class postview class 2}>
</div>
);
}
const PostContainer = props => {
const menu = props.posts.map(post => {
return (
<RenderPost posts={post} />
)
});
return (
<div className="container-fluid">
<div className="row justify-content-center">
<PostView />
{menu}
</div>
</div>
);
}
export default PostContainer;
Class 2 (post View)
class PostView extends Component {
constructor(props) {
super(props);
this.state = {
isModalOpen : true,
}
this.toggleModal = this.toggleModal.bind(this);
}
toggleModal(e) {
e.stopPropagation()
this.setState({
isModalOpen: !this.state.isModalOpen
});
}
render() {
return (
<div className="shadow-sm p-2 mb-2 bg-white">
<div
className="modal"
style={{display: this.state.isModalOpen ? 'block' : 'none' }}
onClick={this.toggleModal} >
<div
className="modal-content"
onClick={ e => e.stopPropagation() } >
<span
className="close"
onClick={this.toggleModal}
>×
</span>
<div className="container">
<h3 className="form-header">Hello</h3>
</div>
</div>
</div>
</div>
);
}
}
export default PostView;
This can be simply acheived by maintaining a state variabe for post click in the parent component and passing it via prop to child.
PostContainer.js
import React, { Component, useState } from 'react';
import '../App.css';
import PostView from './PostView';
const PostContainer = props => {
const [post, setPost] = useState(false);
function RenderPost({posts}) {
return (
<div className="post-holder shadow-sm p-3 mb-4 bg-white rounded" key={posts.id} onClick={setPost(posts)}>
</div>
);
}
const menu = props.posts.map(post => {
return (
<RenderPost posts={post} />
)
});
return (
<div className="container-fluid">
<div className="row justify-content-center">
<PostView post={post} />
{menu}
</div>
</div>
);
}
export default PostContainer;
Constructor function of PostView Component
constructor(props) {
super(props);
this.state = {
isModalOpen : !!props.post,
}
this.toggleModal = this.toggleModal.bind(this);
}
And you can use the same post prop to render post in render function of your child component.
Hope it helps!!
I am trying to generate a random user information when pressing the button, and display the information above the button. In ProfilePanel.js, I created a avatar and user constants, which will use to show the information. In index.js, the avatar constant works for that since it doesn't need to use the Button. however, for user constant, it doesn't work. In below's code, I am fetching a api data to display user name, but it didn't show anything, I am not sure where wrong, is something wrong in Button.js or index.js. and how can I fix it. Can somebody help me out? Thanks.
<Button title="name" >
<p key={contact.name} user={contact.name}></p>
</Button>
index.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Panel from "./ProfilePanel";
import axios from 'axios';
import './index.css';
import Button from './Button';
const url = 'https://randomuser.me/api/';
class App extends Component {
constructor(props) {
super(props);
this.state = {
contacts: []
}
}
componentDidMount() {
this.fetchdata();
}
fetchdata() {
axios.get(url)
.then(res => {
console.log(res);
this.setState({ contacts: res.data.results});
});
}
render(){
const {contacts} = this.state;
return (
<div className="panel">
{contacts.map(contact => (
<div class="panel">
<Panel
key={contact.picture} avatar={contact.picture.medium}
/>
<li class="flex-container">
<Button title="name" >
<p key={contact.name} user={contact.name}></p>
</Button>
<Button title="location" onClick={this.fetchdata}>
</Button>
<Button key={contact.email} title="email">
</Button>
<Button key={contact.phone} title="phone">
</Button>
<Button key={contact.login.password} title="password">
</Button>
</li>
</div>
))}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
ProfilePanel.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import './index.css';
import Button from './Button';
const style={
borderRadius: 150,
margin: 15,
}
class Panel extends Component {
render() {
const { avatar, user } = this.props;
return (
<div className="Panel">
<div class="panels">
<div className="avatar">
<img src={avatar} class="imageStyle" alt="" width={"200%"} height={"auto"}/>
</div>
</div>
<div class="center">
<h2 className="user">{user}</h2>
</div>
</div>
);
}
}
export default Panel;
Button.js
import './index.css';
import React, { Component } from 'react';
class Button extends Component {
constructor(props) {
super(props);
this.state = {
open:false,
};
}
render() {
const { title } = this.props;
const {open} = this.state;
return (
<button className={` ${open ? 'open' : ''}`}
class='button' onClick={(e) => this.handleClick(e)}>
<div className="panel-heading">
<h2 class='buttoncenter'>{title}</h2>
</div>
</button>
);
}
handleClick(e) {
e.preventDefault();
this.setState({
open: this.state.open
})
}
}
export default Button;
You're not changing state in the handle click. You need to set open to true;
handleClick(e) {
e.preventDefault();
this.setState({
open: true
})
}
You need to pass your user information in index.js. I think you have missed to pass the user props to the panel component, so that it shows the avatar alone. Without passing the users props, you are trying to destructure there in panel component.
//index.js should be like this
render(){
const {contacts} = this.state;
return (
<div className="panel">
{contacts.map(contact => (
<div class="panel">
<Panel
key={contact.picture} user={contact.name} avatar={contact.picture.medium}
/>
<li class="flex-container">
<Button title="name" >
<p key={contact.name} user={contact.name}></p>
</Button>
<Button title="location" onClick={this.fetchdata}>
</Button>
<Button key={contact.email} title="email">
</Button>
<Button key={contact.phone} title="phone">
</Button>
<Button key={contact.login.password} title="password">
</Button>
</li>
</div>
))}
</div>
);
}
I am working on the React Recipe Box project from Free Code Camp. I have a parent component that displays recipe names, which it receives as props. The recipe name can be clicked to display a child component that then has the same props passed down to the child component to display information about the ingredients. My problem is, when I click the recipe name, the props in the parent component become undefined. Ive googled a bunch, but I cant figure out why this is happening. Has anyone run into this before?
import React, { Component } from 'react';
import ShowRecipe from './recipeDetail';
class RecipeDetail extends Component {
constructor(props){
super(props)
this.state = {
isHidden:true
}
}
render() {
return (
<div className="card">
<div className="card-header">
<h5>
<button
className="btn btn-link"
onClick={() => {
this.setState({isHidden: !this.state.isHidden})
}}
>
{this.props.recipe.recipeName}
</button>
</h5>
</div>
{ !this.state.isHidden &&
<ShowRecipe
ingredients={this.props.recipe.ingredientsList}
/>
}
</div>
);
}
}
export default RecipeDetail;
this is where the props for RecipeDetail are coming from:
import React from 'react';
import RecipeDetail from './recipeDetail';
import { Jumbotron, ListGroup } from 'react-bootstrap';
const RecipeBoxHolder = ({recipes}) =>{
const recipesItems = recipes.map((recipe) => {
console.log(recipes);
return(
<RecipeDetail
key={recipe.recipeName}
recipe={recipe}
/>
)
})
return(
<div>
<Jumbotron className="jtron">
<h5>Recipes</h5>
<ListGroup>
{recipesItems}
</ListGroup>
</Jumbotron>
</div>
)
}
export default RecipeBoxHolder;
TypeError: Cannot read property 'recipeName' of undefined
RecipeDetail.render
src/components/recipeDetail.js:22
19 | <button
20 | className="btn btn-link"
21 | onClick={() => {this.setState({isHidden:
!this.state.isHidden})}} >
> 22 | {this.props.recipe.recipeName}
23 | </button>
24 |
25 | </h5>
this comes from a different component where the user enters inputs and its passed to the main parent component for the app
let recipeObject={
recipeName: this.state.recipe,
ingredientsList: this.state.ingredients
};
component where user inputs data, takes data and passes it back up to main parent component
import React, {Component} from 'react';
import {ListGroupItem} from 'react-bootstrap';
class AddModal extends Component{
constructor(props){
super(props)
this.state={
recipe: '',
ingredients: ''
}
}
render(){
let recipeObject={
recipeName: this.state.recipe,
ingredientsList: this.state.ingredients
};
return(
<ListGroupItem>
<div className="card">
<div className="card-header">
<span>Add Recipe</span> <i className="fas fa-utensils"></i>
</div>
<div className="card-body">
<label>Recipe</label>
<input value={this.state.recipe} onChange={event =>
this.setState({recipe: event.target.value})} type="text"
className="form-control"/>
<label className="add-ingredients-label">Add Ingredients</label>
<textarea value={this.state.ingredients} onChange={event =>
this.setState({ingredients: event.target.value})}
className="form-control"></textarea>
</div>
<div className="card-footer">
<button className="close-button btn btn-outline-danger btn-
sm">
Close
<i className="fas fa-times"></i>
</button>
<button onClick={e =>{this.props.addRecipe(recipeObject)}}
className="btn btn-outline-success btn-sm">
Add Recipe
<i className="fas fa-plus"></i>
</button>
</div>
</div>
</ListGroupItem>
);
}
}
export default AddModal;
this is the main parent component:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import RecipeBoxHolder from './components/recipeContainer';
import AddModal from './components/addRecipeModal';
import './style/index.css';
import {Button, Modal} from 'react-bootstrap';
class App extends Component{
constructor(props){
super(props);
this.state={
recipes:[],
showAddModal: false
}
}
render(){
const addRecipe = (recipeObject) =>{
this.setState(prevState => ({
recipes:[...prevState.recipes, recipeObject]}
))
}
return(
<div className="container">
<RecipeBoxHolder recipes={this.state.recipes} />
<Button
bsStyle="primary"
bsSize="large">
</Button>
<AddModal addRecipe={addRecipe} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
#Esteban Trevino I put my findings into an answer because the comments become too long, however I do not have a solution as I could not reproduce your issue.
I copied your code into a create-react-app generated react scaffold, and it runs fine. In this gist you can see the file I made:
https://gist.github.com/femans/22324382a8e04390f6a0ece49b867708
I do want to point out that you should not use the recipeName as a key, because they are not unique by design. Better use the following construction:
const recipesItems = recipes.map((recipe, key) => {
return(
<RecipeDetail
key={key}
recipe={recipe}
...