When I press enter key I get this error: Form submission canceled because form is not connected. But Add User button work right.
function Form({ modal, showModal, onAddUser }) {
const [name, setName] = useState('');
const [surname, setSurname] = useState('');
const [age, setAge] = useState('');
const createUser = (e) => {
if (!name || !age || !surname) {
return;
} else {
onAddUser(name, age, surname)
console.log(name, age)
}
setName('');
setAge('')
setSurname('');
showModal(false)
console.log(e);
}
return (
<>
{modal ? <FormContainer onSubmit={createUser}>
<InputContainer value={name} onChange={(e) => setName(e.target.value)} type="text" placeholder="Enter name" />
<InputContainer value={age} onChange={(e) => setAge(e.target.value)} type="number" placeholder="Enter age" />
<InputContainer value={surname} onChange={(e) => setSurname(e.target.value)} type="text" placeholder="Enter surname" />
<Button style={{ marginRight: "10px" }} type="cancel" onClick={() => showModal(false)}>Cancel</Button>
<Button type="button">Add User</Button>
</FormContainer>
: (null)
}
</ >
)
}
You should change the "Add User" button type to submit instead of button.
Also, you should call e.preventDefault() on the form submission.
Your component should be like this,
function Form({ modal, showModal, onAddUser }) {
const [name, setName] = useState('');
const [surname, setSurname] = useState('');
const [age, setAge] = useState('');
const createUser = (e) => {
// add this line here to prevent `form` element default behaviour
e.preventDefault();
if (!name || !age || !surname) {
return;
} else {
onAddUser(name, age, surname)
console.log(name, age)
}
setName('');
setAge('')
setSurname('');
showModal(false)
console.log(e);
}
return (
<>
{modal ? <FormContainer onSubmit={createUser}>
<InputContainer value={name} onChange={(e) => setName(e.target.value)} type="text" placeholder="Enter name" />
<InputContainer value={age} onChange={(e) => setAge(e.target.value)} type="number" placeholder="Enter age" />
<InputContainer value={surname} onChange={(e) => setSurname(e.target.value)} type="text" placeholder="Enter surname" />
<Button style={{ marginRight: "10px" }} type="cancel" onClick={() => showModal(false)}>Cancel</Button>
{/* here we changed type="button" to type="submit" */}
<Button type="submit">Add User</Button>
</FormContainer>
: (null)
}
</ >
)
}
Related
I have 4 input fields.
username,password,email,mobile.
if we type # in username field, the email field should disappear and if we type any digit in username then mobile field should disappear.
Help me with this.
I have created very basic code but not sure how to achive this.
import { useState } from "react";
export default function App() {
const [name, setname] = useState("");
const [password, setPassword] = useState("");
const [show, setShow] = useState();
if (name.includes("#")) {
setShow( ) //stuck here
}
return (
<>
<input
type="text"
placeholder="username"
value={name}
onChange={(e) => setname(e.target.value)}
/>
<input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<input type="email" placeholder="email" id="email" />
<input type="number" placeholder="number" />
</>
);
}
Try below code
import { useState } from "react";
export default function App() {
const [name, setname] = useState("");
const [password, setPassword] = useState("");
//****************************************
const [show, setShow] = useState(false);
//****************************************
if (name.includes("#")) {
//****************************************
setShow(true) //stuck here
//****************************************
}
return (
<>
<input
type="text"
placeholder="username"
value={name}
onChange={(e) => setname(e.target.value)}
/>
<input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
//****************************************
{ show?
<input type="email" placeholder="email" id="email" />:''}
//****************************************
<input type="number" placeholder="number" />
</>
);
}
we can use with && operator if our show is false then it hide on screen
import { useState } from "react";
export default function App() {
const [name, setname] = useState("");
const [password, setPassword] = useState("");
const [show, setShow] = useState(true);
if (name.includes("#")) {
setShow(false) //stuck here
}
return (
<>
<input
type="text"
placeholder="username"
value={name}
onChange={(e) => setname(e.target.value)}
/>
<input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{show && <input type="email" placeholder="email" id="email" />}
<input type="number" placeholder="number" />
</>
);
}
I'll give example with the email - you can apply same approach for the phone.
Some notes: you do not need a separate state whether to show email filed, it is derived from the name state.
Advice: You should not change state during the function render. You can do that on a user interaction (onClick, onChange etc) or useEffect hook if data fetch or another side effect.
The solution is simple
import { useState } from "react";
export default function App() {
const [name, setname] = useState("");
const [password, setPassword] = useState("");
const [show, setShow] = useState();
const showEmailField = name.includes("#"); //derived computed value base on the name state
return (
<>
<input
type="text"
placeholder="username"
value={name}
onChange={(e) => setname(e.target.value)}
/>
<input
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{ showEmailField && <input type="email" placeholder="email" id="email" /> }
<input type="number" placeholder="number" />
</>
);
}
you can try using the useEffect hook.
try something like this .
in this way you can keep track of everytime name changes
useEffect(() => {
if (name.includes("#")) {
setShow();
}
}, [name]);
and then
show && (
<input
type="text"
placeholder="username"
value={name}
onChange={(e) => setname(e.target.value)}
/>
)
I am stuck on getting my Modal to appear when the Edit button is clicked. I can see that the modalShow is being set to true when I click the button since I console.log the value. Any idea on how to get modal to appear when user clicks the edit button?
product-screen.jsx
import { useState } from 'react';
import { Button, Card, Form, Modal } from 'react-bootstrap';
import 'reactjs-popup/dist/index.css';
import ModalScreen from './modal-screen';
const ProductCardScreen = ({ product }) => {
const { productName, productPrice, productInStock, productDescription } =
product;
const [modalShow, setModalShow] = useState(false);
const [updateProduct, setProductUpdate] = useState({
productDescription,
productInStock,
productName,
productPrice,
});
const handleChange = (event) => {
console.log('event', event.target.value);
setProductUpdate({
...updateProduct,
[event.target.name]: event.target.value,
});
};
// todo
const saveUpdatedProduct = (product) => {
// save logic here to db
};
const handleDelete = () => {
alert('Are you sure you want to delete?');
};
const handleModalChange = () => {
console.log('called');
setModalShow(true);
};
return (
<div>
<Card>
<Card.Body>
<Card.Title>Product Name: {product.productName}</Card.Title>
<Card.Text>
Product Description: {product.productDescription}
</Card.Text>
<Card.Text>Product Quantity: {product.productInStock}</Card.Text>
<Card.Text>Price: ${product.productPrice}</Card.Text>
<div
style={{
float: 'right',
}}
>
<Button
style={{ margin: '10px' }}
onClick={() => handleModalChange()}
className='btn btn-primary'
variant='primary'
>
Edit
</Button>
<Button
onClick={() => handleDelete()}
className='btn btn-danger'
variant='primary'
>
Delete
</Button>
</div>
</Card.Body>
</Card>
<ModalScreen product={product} handleChange={handleChange} />
</div>
);
};
export default ProductCardScreen;
modal-screen.jsx
import { Button, Form, Modal } from 'react-bootstrap';
const ModalScreen = ({ product, handleChange }) => {
const { productName, productPrice, productInStock, productDescription } =
product;
return (
<Modal
animation={false}
size='lg'
aria-labelledby='contained-modal-title-vcenter'
centered
>
<Modal.Header>
<Modal.Title id='contained-modal-title-vcenter'>
Product Information
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group className='mb-3'>
<Form.Label>Name</Form.Label>
<Form.Control
onChange={handleChange}
value={productName}
type='text'
/>
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Description</Form.Label>
<Form.Control
onChange={handleChange}
value={productDescription}
type='text'
/>
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Amount In Stock</Form.Label>
<Form.Control
onChange={handleChange}
value={productInStock}
type='text'
/>
</Form.Group>
<Form.Group className='mb-3'>
<Form.Label>Price</Form.Label>
<Form.Control
onChange={handleChange}
value={`$${productPrice}`}
type='text'
/>
</Form.Group>
<Button variant='primary' type='button'>
Save
</Button>
<Button variant='danger' type='button'>
Cancel
</Button>
</Form>
</Modal.Body>
<Modal.Footer></Modal.Footer>
</Modal>
);
};
export default ModalScreen;
I would like to open a modal with a click. The button is visible on my page, but unfortunately the modal does not open when you click it.
Unfortunately I haven't found a solution yet how I can solve the problem.
function ModalPage(props) {
const [username, setUsername] = useState();
const [password, setPassword] = useState();
const [showModal, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<MDBContainer>
<MDBBtn rounded onClick={handleShow}>+</MDBBtn>
<MDBModal show={showModal} onHide={handleClose}>
<MDBModalHeader className="text-center" titleClass="w-100 font-weight-bold">Data Input</MDBModalHeader>
<MDBModalBody>
<form className="mx-3 grey-text">
<MDBInput label="ID" group type="text" validate />
<MDBInput label="Username" group type="email" validate error="wrong" success="right"
onChange={(evt) => setUsername(evt.target.value)}
/>
<MDBInput label="Password" group type="text"
onChange={(evt) => setPassword(evt.target.value)}
/>
<MDBInput type="textarea" rows="2" label="Your message" />
</form>
</MDBModalBody>
<MDBModalFooter className="justify-content-center">
<MDBBtn color="unique" onClick={handleClose}>Send
<MDBIcon far icon="paper-plane" className="ml-2" />
</MDBBtn>
</MDBModalFooter>
</MDBModal>
</MDBContainer>
);
}
export default withRouter(ModalPage);
According to this MDB page, the property used to dictate whether or not a modal is being shown is not show, but rather isOpen. Change
<MDBModal show={showModal} onHide={handleClose}>
To
<MDBModal isOpen={showModal} onHide={handleClose}>
I am trying to get updated textbox value if I have props from parent component.
Here is my code -
const initialState = {
name: "",
age: 0
}
const UserAdd = (props:any) => {
const {selectedUser} = props;
const [state, setState] = useState<any>(initialState);
const dispatch = useDispatch();
const onChangeValue = (event:any) => {
const { name, value } = event.target;
setState((prevState:any) => (
{ ...prevState, [name]: value }
));
}
const onSubmit = (e:any) => {
e.preventDefault();
const { name } = e.target;
dispatch(addUser(state.name, state.age))
setState({ ...initialState });
}
const onUpdate = (e:any) => {
e.preventDefault();
const { name } = e.target;
console.log(state.name , state.age , "name-age")
}
return (
<div className="add-user">
<hr />
<h2>Add User</h2>
<hr />
{ selectedUser ?
(<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={selectedUser.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={selectedUser.age} />
<button type="submit" onClick={onUpdate} className="btn btn-primary col-md-2 mb-2">Update</button>
</form>)
:
(
<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={state.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={state.age} />
<button type="submit" onClick={onSubmit} className="btn btn-primary col-md-2 mb-2">Submit</button>
</form>
)
}
</div>
)
}
export default UserAdd;
When There is no props i.e (no selectedUser), Then state change working fine and I am able to dispatch action as well. But When Props(selectedUser) is available, then I am unable to edit textbox field & unable to get updated state. Please someone help me.
look deeper into your code.. there is different forms when selectedUser is not falsy...
when there is selected user:
value={selectedUser.name}
shouled be like the other form's input:
value={state.name}
u also need to add useEffect to change the state when the prop is being changed, u can do it like so:
useEffect(()=>{
setState((prevState:any) => (
{ ...prevState, name: selectedUser.name}
));
},[selectedUser.name])
the useEffect then will be executed whenever any item of the dependency list will be different from the last render (in this case each time the selectedUser.name is being changed)
In case there is selectedUser, the value attribute of input field should be reactive. Currently, it is set to props that won't react to onChangeValue handler. Use state in the value attribute (state.name /state.age) and initialize states with selectedUser props value. It might look like below -
const initialState = {
name: "",
age: 0
}
const UserAdd = (props:any) => {
const {selectedUser} = props;
//Button text would be Update or submit that depends on selectedUser.
const btnText = selectedUser ? "Update" : "Submit";
//If it founds selected user then set to it otherwise go with initialState
const [state, setState] = useState<any>(selectedUser || initialState);
const dispatch = useDispatch();
const onChangeValue = (event:any) => {
const { name, value } = event.target;
setState((prevState:any) => (
{ ...prevState, [name]: value }
));
}
const onSubmit = (e:any) => {
e.preventDefault();
const { name } = e.target;
dispatch(addUser(state.name, state.age))
setState({ ...initialState });
}
return (
<div className="add-user">
<hr />
<h2>Add User</h2>
<hr />
<form className="form-inline">
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="email2" placeholder="Enter user name" name="name" value={selectedUser.name} onChange={onChangeValue} />
<input type="text" className="form-control mb-2 mr-sm-5 col-md-4" id="pwd2" placeholder="Enter user age" name="age" onChange={onChangeValue} value={selectedUser.age} />
<button type="submit" onClick={onUpdate} className="btn btn-primary col-md-2 mb-2">{btnText}</button>
</form>
</div>
)
}
export default UserAdd;
Once you set it up like this you don't need to render forms conditionally. you only need one form. I have added a few comments as well. I have created a working POC here https://codesandbox.io/s/propstostate-react-ssirn
I have a FieldArray in Redux Form that I push objects inside this array and right after that I have a callback to trigger a function to make a POST request.
When I submit the form I get the old values, because the push() method of the Redux Form is an async dispach from redux.
// Parent component
<FieldArray
name="myObjects"
component={ChildComponent}
onSubmitObjects={this.onSubmit} />
// Function
onSubmit = async () => {
const { getFormValues } = this.props;
const data = {
myObjects: getFormValues.myObjects
}
try {
// const contact = await Service.updateUser(data);
} catch (e) {
console.log(e)
}
}
I need to submit the form with the new values added in the array right after the push method.
// Function inside ChildComponent
addNewObject = () => {
const { fields, onSubmitObjects} = this.props;
fields.push({
number: 1,
name: 'Foo',
});
if (onSubmitObjects) {
onSubmitObjects(); // cb() to trigger a function in the parent component
}
}
Is there a way to call the callback with the new values right after the push method?
You should use a form with redux-form handleSubmit to wrap your FieldArray. You can optionally pass your custom submit function (to make API requests, submit validation, etc.) to handleSubmit so it'd look like this <form onSubmit={handleSubmit(this.onSubmit)}> ...
See this example from redux-form official docs:
FieldArraysForm.js
import React from 'react'
import {Field, FieldArray, reduxForm} from 'redux-form'
import validate from './validate'
const renderField = ({input, label, type, meta: {touched, error}}) => (
<div>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderHobbies = ({fields, meta: {error}}) => (
<ul>
<li>
<button type="button" onClick={() => fields.push()}>Add Hobby</button>
</li>
{fields.map((hobby, index) => (
<li key={index}>
<button
type="button"
title="Remove Hobby"
onClick={() => fields.remove(index)}
/>
<Field
name={hobby}
type="text"
component={renderField}
label={`Hobby #${index + 1}`}
/>
</li>
))}
{error && <li className="error">{error}</li>}
</ul>
)
const renderMembers = ({fields, meta: {error, submitFailed}}) => (
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>Add Member</button>
{submitFailed && error && <span>{error}</span>}
</li>
{fields.map((member, index) => (
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}
/>
<h4>Member #{index + 1}</h4>
<Field
name={`${member}.firstName`}
type="text"
component={renderField}
label="First Name"
/>
<Field
name={`${member}.lastName`}
type="text"
component={renderField}
label="Last Name"
/>
<FieldArray name={`${member}.hobbies`} component={renderHobbies} />
</li>
))}
</ul>
)
const FieldArraysForm = props => {
const {handleSubmit, pristine, reset, submitting} = props
return (
<form onSubmit={handleSubmit}>
<Field
name="clubName"
type="text"
component={renderField}
label="Club Name"
/>
<FieldArray name="members" component={renderMembers} />
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
)
}
export default reduxForm({
form: 'fieldArrays', // a unique identifier for this form
})(FieldArraysForm)