I am creating a modal in React that will allow users to add an item to a table. I create a RecipeModal component with a form inside, and I receive no errors when compiling, yet nothing happens when I click the button. I followed a tutorial very closely and have run out of ideas. I've seen people have issues with 'fade' in React that turns the modal completely clear and therefor invisible, but I tried checking in "Inspect" (DevTools? I'm am not sure what it is called) for 'modal' and didn't see it there. I am very new to web developing, so let me know if I should attach something else. I had more input field, but removed them while trying to fix this.
import React, { Component } from 'react';
import { Button, Modal, ModalHeader, ModalBody, Form, FormGroup, Label, Input } from 'reactstrap';
import { connect } from 'react-redux';
import { addRecipe } from '../action/recipeActions';
class RecipeModal extends Component {
state = {
modal: false,
recipe_name: '',
recipe_description: '',
recipe_ingredients: '',
recipe_steps: ''
}
toggle = (e) => {
this.setState({
modal: !this.state.modal
});
}
onChange = (e) => {
this.setState(
{ [e.target.recipe_name]: e.target.value }
);
}
render() {
return (
<div>
<Button
color="dark"
style={{ marginBotton: '2rem' }}
onClick={this.toggle}
>Add Recipe</Button>
<Modal
isOpen={this.state.Modal}
toggle={this.toggle} >
<ModalHeader toggle={this.toggle}>Add a New Recipe</ModalHeader>
<ModalBody>
<Form onSubmit={this.onSubmit}>
<FormGroup>
<Label for="recipe">Recipe</Label>
<Input
type="text"
recipe_name="recipe_name"
id="recipe"
placeholder="Add recipe name"
OnChange={this.onChange} />
</FormGroup>
</Form>
</ModalBody>
</Modal>
</div>
);
}
}
export default connect()(RecipeModal);
State is case-sensitive. So it's either you rename your modal state to Modal
state = {
Modal: false,
...
};
or refactor the isOpen prop to <Modal isOpen={this.state.modal} toggle={this.toggle}>
I suggest the latter.
Related
I am at my wits end understanding why my submit is not working. I literally am using the same code base over and over and with all other parts of my code this was working fine.
Now for the new code part, it simply does not work anymore. I try to simply submit some fields from a form and pass the values from the child component back to the parent component. This is my child component:
import React, { Component } from 'react';
import { Modal, Button, Form } from 'react-bootstrap';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { icon } from '#fortawesome/fontawesome-svg-core/import.macro';
import '../../../components/Modal.css';
class Alert extends Component {
state = {
type: 'price'
}
setType(type) {
this.setState({
type: type
})
}
render() {
const { alert, handleClose, show, header } = this.props
const { type } = this.state
const showHideClassName = show ? "modal display-block" : "modal display-none";
return (
<div className={showHideClassName}>
<section className="modal-main">
<Form onSubmit={alert}>
<Modal.Dialog>
<Modal.Header className="modalTitle">
<Modal.Title>{ header }</Modal.Title><FontAwesomeIcon className="create-alert-icon" icon={icon({name: 'bell', style: 'solid' })} />
</Modal.Header>
<Modal.Body className="modalTitle" >
<Form.Group>
<label htmlFor="type">Type:</label>
<Form.Control
className="modalInput form-control form-control-sm"
name="type"
as="select"
value={type}
onChange={e => {
this.setType(e.target.value);
}}
>
<option value="price">Price</option>
<option value="amount">Amount</option>
</Form.Control>
</Form.Group>
{
alertType === 'price' ?
<span>
<label className="modalTitle" htmlFor="price">Alert me based on price:</label>
<Form.Control className="modalInput" name="amount" id="amount" size="sm" type="text" placeholder="1.22" />
</span>
: <span></span>
}
{
alertType === 'amount' ?
<span>
<label className="modalTitle" htmlFor="amount">Alert me based on amount:</label>
<Form.Control className="modalInput" name="amount" id="amount" size="sm" type="text" placeholder="300" />
</span>
: <span></span>
}
</Modal.Body>
<Modal.Footer>
<Button type="button" onClick={handleClose} variant="secondary">Close</Button>
<Button type="submit" onClick={handleClose} variant="primary">Submit</Button>
</Modal.Footer>
</Modal.Dialog>
</Form>
</section>
</div>
)
}
}
export default Alert;
This is my function in the parent component:
alert = (e) => {
e.preventDefault()
const { user } = this.props
const { amount, type } = e.target
const alert = { name: this.props.name, value: amount, type: type }
console.log(alert);
addAlert(alert);
}
This is my component in the parent that is being called and passes the props:
<CreateAlert show={ showCreateAlertModal } alert={this.alert} handleClose={ this.hideCreateAlertModal } header="Create a new Alert" />
When I submit using the Alert component, the modal is closed and nothing happens, nothing reaches the alert function, I have tried to move the component around in the parent, I have checked if the submit button is inside of the form (yes it is). I have tried to define the function one level above on another parent and pass the function through props two levels down to the child component, I have tried to remove content inside of the child component until there was just the submit button and the form left, and none of these things do any submit, the only thing that works is to define the button as submit directly which causes standard html5 submit to do its work, this works ... so I am really at a loss why react is not able to use the same boilerplate once more to do the same action it did a couple of times before ... I hope someone has an idea for me here, since I am getting mad at this, since everything looks fine ...
The problem is that when you click the Submit button, it will call handleClose and close the modal, and then there will be no Submit button to submit. What you have to do is in your same alert function add the handleSubmit() function.
alert = (e) => {
e.preventDefault()
const { user } = this.props
const { amount, type } = e.target
const alert = { name: this.props.name, value: amount, type: type }
console.log(alert);
addAlert(alert);
handleClose();
}
And leave the Submit button without any onClick handler, since the form itself will trigger the closing and the submitting logic.
I guess I found my issue .... it seems like it is not smart to put e.preventDefault() function inside of the handleClose() function which closes the modal ... guess the handleClose function fired first preventing the submit from working properly :/
I have a model file in which I want to submit form but I am not able to trigger submit function my file is like
import React, { useState, useEffect } from "react";
import InputField from "./InputField";
import Button from "./Button";
import axios from "axios";
import { Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
function ArticleOptionsModal(props) {
const [articleOption, setArticleOption] = useState("");
useEffect(() => {
if (typeof props.articleopt === "undefined") {
setArticleOption("");
console.log("hhiii");
} else {
setArticleOption(props.articleopt.optionname);
console.log("hhiii");
}
}, [props]);
return (
<form onSubmit={e => handleSubmit(e)}>
<Modal isOpen={props.modal} toggle={props.toggle}>
<ModalHeader toggle={props.toggle}>Times</ModalHeader>
<ModalBody>
<div className='row'>
<div className='col-sm-6'>
<div className='form-group text-left'>
<label htmlFor='' className='small'>
Name
</label>
<InputField
name='username'
placeholder={"Enter Name"}
value={articleOption.optionname}
onChange={e => changeOptionName(e)}
/>
</div>
</div>
)}
</ModalBody>
<ModalFooter>
<Button
name={"Submit"}
type='submit'
btnLongWidth={false}
onClick={props.toggle}
/>
<Button
name={"Cancel"}
dangerButton={true}
btnLongWidth={false}
onClick={props.toggle}
/>
</ModalFooter>
</Modal>
</form>
);
function changeOptionName(e) {
setArticleOption(e.target.value);
}
function handleSubmit(e) {
console.log("hiiiiii");
}
}
export default ArticleOptionsModal;
Here handleSubmit is not triggering when I try to submit form. I tried using diff methods to just trigger this submit method but till now no luck . Any kind of help would be highly appreciated.
The form tag needs to be inside the modal component as otherwise, the event doesn´t bubble up correctly.
As commented, I would suggest using a form library to handle the form management.
Personally I suggest Final Form:
https://github.com/final-form/react-final-form
Currently trying to make modals in React and want to make a grid where "button 1" shows "modal 1" and "button 2" shows "modal 2" etc.
At the moment when I press my button to show a modal it shows both modal 1 and modal 2. How do I set it up so button 1 only opens modal 1?
This is my App.js:
import React from 'react';
import './main.css';
import Modal from './components/Modal/modal';
class App extends React.Component {
state = {
show: false
};
showModal = x => {
this.setState({
show: !this.state.show
});
};
render() {
return (
<div>
<div className="button-container">
<button className="toggle-button" onClick={x => {
this.showModal(x);
}}>Show yourself Modal!</button>
</div>
<Modal onClose={this.showModal} show={this.state.show} title="Test modal 1" id="1">Lorem ipsum</Modal>
<Modal onClose={this.showModal} show={this.state.show} title="Test modal 2" id="2">I am a different modal</Modal>
</div>
);
}
}
export default App;
And this is my modal.js component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import './modal.css';
export default class Modal extends Component {
onClose = x => {
this.props.onClose && this.props.onClose(x);
};
render() {
if(!this.props.show) {
return null;
}
return (
<div className="modal-wrapper">
<h2 className="modal-header">{this.props.title}</h2>
<div>{this.props.children}</div>
<div>
<button className="modal-close" onClick={this.onClose}></button>
</div>
</div>
)
}
}
Modal.propTypes = {
onClose: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired
};
The simplest way would be to add a second key to your state so that you have a way to manage showing both modals independently.
class App extends React.Component {
state = {
show1: false,
show2: false
};
Then make your change function to be a curried function that accepts a parameter to update the correct part of state. In order to use a variable to access an object key, we need to access it as an array like this:
showModal = (modal) => (e) => {
this.setState({
[modal]: !this.state[modal]
});
};
Then use it like this:
render() {
return (
<div>
<div className="button-container">
<button className="toggle-button" onClick={this.showModal('show1')}>Show yourself Modal 1!</button>
<button className="toggle-button" onClick={this.showModal('show2')}>Show yourself Modal 2!</button>
</div>
<Modal onClose={this.showModal('show1')} show={this.state.show1} title="Test modal 1" id="1">Lorem ipsum</Modal>
<Modal onClose={this.showModal('show2')} show={this.state.show2} title="Test modal 2" id="2">I am a different modal</Modal>
</div>
);
}
}
At the moment you have nothing in your state to tell which modal to show. You're using this.state.show to control the visibility of both modals.
You could introduce a state property in your App component which is used to choose which modal to show. For example, passing in a modalId or similar to your click handler. (disclaimer: untested syntax, but the principal is right!)
So your state may look like this:
state = {
[
{
id: 1,
show: false
},
{
id: 2,
show: false
},
]
}
Then, in your click handler, you'd need to pass in the id of the modal to show / hide. You'd need to determine this from something in your UI.
showModal(id) => {
this.setState({
[id]: !this.state[id].show
})
}
If you require Button for every Modal and control its behaviour with it then you can introduce a state to the modal component itself by doing something like this:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import './modal.css';
export default class Modal extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false,
}
}
toggleShow = () => {
const { showModal } = this.state;
this.setState({showModal: !showModal})
};
render() {
const { showModal } = this.state;
return (
<div className="modal-wrapper">
{ showModal &&
<div>
<h2 className="modal-header">{this.props.title}</h2>
<div>{this.props.children}</div>
</div>
}
<div>
<button className="modal-close" onClick={() => this.toggleShow()}>{this.props.btnText}</button>
</div>
</div>
)
}
}
It can be build further to alter the default behaviour. This should clean your App.js code.
I'm new to React and I'm trying to create a modal that will be used to display different content. I have currently set this up to have different 'mode' states and each mode will display different markup, which gets passed into the Modal component as this.props.children
It seems to work to an certain extent but I'm having problems with state handling. The input works fine updating and displaying the current state of the input, but once this content is nested within the Modal component it does some strange things like no longer allowing you to type or show any key input at all, if there is state content, any keypresses are only updating the state with the last character.
I'm guessing this is because the Modal is a stateful component with a new construtor, the reference to 'this' and the 'handleChange' function within the parent app is lost.
Any ideas on where I'm going wrong or how to properly go about this?
Cheers guys :)
The code is here:
import React, { Component } from "react";
import ReactDOM from "react-dom";
class Modal extends Component {
shouldComponentUpdate(nextProps, nextState) {
return nextProps.show !== this.props.show;
}
componentWillUpdate() {
console.log("[Modal] WillUpdate");
}
render() {
return (
<div
style={{
transform: this.props.show ? "translateY(0)" : "translateY(-100vh)",
opacity: this.props.show ? "1" : "0",
padding: "20px",
border: "1px solid"
}}
>
{this.props.children}
</div>
);
}
}
class App extends Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
this.state = {
newItem: "",
modalOpen: false,
modalMode: ""
};
}
handleChange(e) {
const target = e.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
openModal(mode) {
this.setState({
modalOpen: true,
modalMode: mode
});
}
render() {
console.log(this.state);
let modalContent;
switch (this.state.modalMode) {
case 'addItem':
modalContent = (
<form>
<h1>Add Item</h1>
<input
type="text"
name="newItem"
value={this.state.newItem}
placeholder="Enter an item"
onChange={this.handleChange}
/>
</form>
)
break;
case 'editItem':
modalContent = (
<div>
</div>
)
break;
default:
modalContent = (null)
}
return (
<div>
<button onClick={() => this.openModal('addItem')}>Open Modal</button>
<Modal show={this.state.modalOpen}>
{modalContent}
</Modal>
<h2>Same inputs outside modal</h2>
<input
type="text"
name="newItem"
value={this.state.newItem}
placeholder="Enter an item"
onChange={this.handleChange}
/>
<input
type="text"
name="newItem"
value={this.state.newItem}
placeholder="Enter an item"
onChange={this.handleChange}
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And a code sandbox here - https://codesandbox.io/s/qzx14rmy6
Easy and fast fix could be remove the "value" sync and the state will update just fine.
<input type="text" name="newItem" placeholder="Enter an item" onChange={this.handleChange} />
I pointed out the real problem. In "shouldComponentUpdate", if you return "false" for any reason, component in NOT update with state. This method should be used carefully as it can break your app. Remove the method or return true and component will work just fine.
I had a <div> with a <p> child component I name SocialPostwithCSS, and onClick would cause it to hide, change the state to editing: true and a <textarea> would show up and I would use this.textarea.focus with the textarea having ref={(input)=>{this.textarea=input}} and had no problem.
I needed to autosize the area, so I downloaded the NPM package TextareaAutosize, now I'm having issues focusing on this text area. I check, the npm TextareAutosize file has class and is not stateless. Currently this.textarea is returning undefined
Summary: How can I focus on <TextareaAutosize /> after <div> onClick
which causes the state change to show <TextareaAutosize>?
File Below:
import React, { Component } from 'react'
import SocialPostWithCSS from './SocialPostWithCSS'
import TextareaAutosize from 'react-autosize-textarea'
class SocialPost extends Component {
constructor(props) {
super(props)
this.state = {
message: this.props.socialPost.message,
editing: false
}
}
_clickToEdit() {
this.textarea.focus()
}
render() {
return (
<div>
{(!this.state.editing) ?
<div onClick={async ()=>{await this.setState({editing: true});this._clickToEdit}}>
<SocialPostWithCSS >{this.state.message}</SocialPostWithCSS>
</div>
:<div>
<TextareaAutosize
onBlur={() => {this.setState({ editing: false})}}
type='text'
ref={(input)=>{this.textarea=input}}
value={this.state.message}
/>
</div>
}
</div>
)
}
}
export default SocialPost
From looking at the code, it appears that TextareaAutosize exposes the inner ref through a prop named innerRef. So change your code like this:
<TextareaAutosize
onBlur={() => {this.setState({ editing: false})}}
type='text'
innerRef={(input)=>{this.textarea=input}}
value={this.state.message}
/>
this.textarea is returning undefined onclick to the div because initially when this.state.editing is false TextareaAutosize component was not rendered and hence this.textarea was not initialized by the mounted instance of the component.