Form in React + Formik + Material Ui (dialog + stepper) - reactjs

My form worked 2 days ago when i didn't implement material UI with dialog and stepper. All the data was sent to the database.
Now i did it, It doesn't want to send at all, i don't even have a mistake in the console or network tab in dev tools. You'll find below at first what that doesnt work. In second where that work.
Thanks a lot in advance.
import { Formik, Form, Field } from 'formik';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogContent from '#material-ui/core/DialogContent';
import DialogTitle from '#material-ui/core/DialogTitle';
import MobileStepper from '#material-ui/core/MobileStepper';
import KeyboardArrowLeft from '#material-ui/icons/KeyboardArrowLeft';
import KeyboardArrowRight from '#material-ui/icons/KeyboardArrowRight';
import axios from 'axios';
import Tools from './Tools';
import Technos from './Technos';
import Clients from './Clients';
import Managers from './Managers';
import Collaborators from './Collaborators';
const validate = (values) => {
const errors = {};
if (values.project_name === '') {
errors.project_name = 'Please choose at least a project name';
} else if (values.end_date < values.start_date) {
errors.end_date = 'The end date has to be after the start date';
}
return errors;
};
const useStyles = makeStyles(() => ({
steps: {
maxWidth: 400,
flexGrow: 1,
},
ModalDialog: {
height: '682px',
width: '683px',
},
}));
const CreateProject = () => {
const [tools, setTools] = useState([]);
const [toolsSelected, setToolsSelected] = useState([]);
const [newTool, setNewTool] = useState('');
const [technos, setTechnos] = useState([]);
const [technosSelected, setTechnosSelected] = useState([]);
const [newTechno, setNewTechno] = useState('');
const [clients, setClients] = useState([]);
const [clientSelected, setClientSelected] = useState();
const [newClient, setNewClient] = useState('');
const [collaborators, setCollaborators] = useState([]);
const [collaboratorsSelected, setCollaboratorsSelected] = useState([]);
const [managers, setManagers] = useState([]);
const [manager, setManager] = useState();
const [projectNameExist, setProjectNameExist] = useState(false);
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const [activeStep, setActiveStep] = React.useState(0);
const submitting = (values, actions) => {
const data = { ...values };
data.ClientId = parseInt(clientSelected, 10);
data.Tools = toolsSelected;
data.Technos = technosSelected;
data.ManagerId = manager;
data.Collaborators = collaboratorsSelected;
axios({
method: 'post',
url: 'http://localhost:5000/api/projects',
data,
})
.then(() => actions.setSubmitting(false))
// .then(() => history.push('/'))
.catch((err) => {
if (err.response.status === 409) {
setProjectNameExist(true);
}
});
};
useEffect(() => {
axios
.get('http://localhost:5000/api/tools')
.then((res) => res.data)
.then((data) => setTools(data));
}, []);
const handleNext = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<Formik
validate={validate}
validateOnChange={false}
validateOnBlur={false}
onSubmit={submitting}
initialValues={{
project_name: '',
start_date: undefined,
end_date: undefined,
teams_link: '',
number_sprints: undefined,
duration_sprints: undefined,
description: '',
time_budget_type: undefined,
project_type: undefined,
signed: undefined,
chance_winning: 0,
}}
>
{({ errors }) => (
<Form id="formCreateProject">
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Create Project
</Button>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog">
<DialogTitle id="form-dialog">Create a project</DialogTitle>
<div className={classes.ModalDialog}>
<DialogContent>
{activeStep === 0 ? (
<div style={{ visibility: activeStep !== 0 ? 'hidden' : '' }}>
<label htmlFor="project_name">Project Name:</label>
<Field
type="text"
id="project_name"
name="project_name"
placeholder="Your project name"
/>
{errors.project_name && <p>{errors.project_name}</p>}
{projectNameExist ? <p style={{ color: 'white' }}>This project name already exist</p> : ''}
<label htmlFor="project_type">Project type:</label>
<Field name="project_type" id="project_type" as="select">
<option value="">--Please choose an option--</option>
<option value="Development">Development</option>
<option value="Design">Design</option>
<option value="Cybersecurity">Cybersecurity</option>
<option value="Cloud">Cloud</option>
</Field>
<Clients
clients={clients}
clientSelected={clientSelected}
newClient={newClient}
setNewClient={setNewClient}
setClientSelected={setClientSelected}
setClients={setClients}
/>
<Managers
managers={managers}
setManagers={setManagers}
setManager={setManager}
/>
<label htmlFor="signed">Is the contract signed?</label>
<Field name="signed" id="signed" as="select">
<option value="">--Please choose an option--</option>
<option value="0">False</option>
<option value="1">True</option>
</Field>
</div>
) : '' }
{activeStep === 1 ? (
<div style={{ visibility: activeStep !== 1 ? 'hidden' : '' }}>
Step 2
<label htmlFor="description">Description:</label>
<Field
type="text"
id="description"
name="description"
placeholder="Short description of the project, what was sold, what are the expectations of the client?"
rows="4"
cols="50"
/>
<label htmlFor="teams_link">Teams link:</label>
<Field
type="text"
id="teams_link"
name="teams_link"
placeholder="Your teams link"
/>
</div>
) : '' }
{activeStep === 2 ? (
<div style={{ visibility: activeStep !== 2 ? 'hidden' : '' }}>
ddsdsds
</div>
) : '' }
{activeStep === 3 ? (
<div style={{ visibility: activeStep !== 3 ? 'hidden' : '' }}>
<Tools
tools={tools}
toolsSelected={toolsSelected}
newTool={newTool}
setNewTool={setNewTool}
setToolsSelected={setToolsSelected}
setTools={setTools}
/>
<Technos
technos={technos}
technosSelected={technosSelected}
newTechno={newTechno}
setNewTechno={setNewTechno}
setTechnosSelected={setTechnosSelected}
setTechnos={setTechnos}
/>
</div>
) : '' }
{activeStep === 4 ? (
<div style={{ visibility: activeStep !== 4 ? 'hidden' : '' }}>
Step 5
<Collaborators
collaborators={collaborators}
setCollaborators={setCollaborators}
collaboratorsSelected={collaboratorsSelected}
setCollaboratorsSelected={setCollaboratorsSelected}
/>
<button type="submit">
Create
</button>
</div>
) : '' }
</DialogContent>
</div>
<MobileStepper
variant="dots"
steps={5}
position="static"
activeStep={activeStep}
className={classes.steps}
nextButton={(
<Button size="small" onClick={handleNext} disabled={activeStep === 4}>
Next
{theme.direction === 'rtl' ? <KeyboardArrowLeft /> : <KeyboardArrowRight />}
</Button>
)}
backButton={(
<Button size="small" onClick={handleBack} disabled={activeStep === 0}>
{theme.direction === 'rtl' ? <KeyboardArrowRight /> : <KeyboardArrowLeft />}
Back
</Button>
)}
/>
</Dialog>
</Form>
)}
</Formik>
);
};
export default CreateProject;```
The previous component where that worked is here:
```import React, { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Formik, Form, Field } from 'formik';
import axios from 'axios';
import Tools from './Tools';
import Technos from './Technos';
import Clients from './Clients';
import Managers from './Managers';
import Collaborators from './Collaborators';
const validate = (values) => {
const errors = {};
if (values.project_name === '') {
errors.project_name = 'Please choose at least a project name';
} else if (values.end_date < values.start_date) {
errors.end_date = 'The end date has to be after the start date';
}
return errors;
};
const NewProject = () => {
const history = useHistory();
const [tools, setTools] = useState([]);
const [toolsSelected, setToolsSelected] = useState([]);
const [newTool, setNewTool] = useState('');
const [technos, setTechnos] = useState([]);
const [technosSelected, setTechnosSelected] = useState([]);
const [newTechno, setNewTechno] = useState('');
const [clients, setClients] = useState([]);
const [clientSelected, setClientSelected] = useState();
const [newClient, setNewClient] = useState('');
const [collaborators, setCollaborators] = useState([]);
const [collaboratorsSelected, setCollaboratorsSelected] = useState([]);
const [managers, setManagers] = useState([]);
const [manager, setManager] = useState();
const [projectNameExist, setProjectNameExist] = useState(false);
const submitting = (values, actions) => {
const data = { ...values };
data.ClientId = parseInt(clientSelected, 10);
data.Tools = toolsSelected;
data.Technos = technosSelected;
data.ManagerId = manager;
data.Collaborators = collaboratorsSelected;
axios({
method: 'post',
url: 'http://localhost:5000/api/projects',
data,
})
.then(() => actions.setSubmitting(false))
.then(() => history.push('/'))
.catch((err) => {
if (err.response.status === 409) {
setProjectNameExist(true);
}
});
};
useEffect(() => {
axios
.get('http://localhost:5000/api/tools')
.then((res) => res.data)
.then((data) => setTools(data));
}, []);
return (
<div id="newProject">
<Formik
validate={validate}
validateOnChange={false}
validateOnBlur={false}
onSubmit={submitting}
initialValues={{
project_name: '',
start_date: undefined,
end_date: undefined,
teams_link: '',
number_sprints: undefined,
duration_sprints: undefined,
description: '',
time_budget_type: undefined,
project_type: undefined,
signed: undefined,
chance_winning: 0,
}}
>
{({ errors }) => (
<Form id="formCreateProject">
<h1 style={{ color: 'white' }}>Create a project</h1>
<label htmlFor="project_name">Project Name:</label>
<Field
type="text"
id="project_name"
name="project_name"
placeholder="Your project name"
/>
{errors.project_name && <p>{errors.project_name}</p>}
{projectNameExist ? <p style={{ color: 'white' }}>This project name already exist</p> : ''}
<Clients
clients={clients}
clientSelected={clientSelected}
newClient={newClient}
setNewClient={setNewClient}
setClientSelected={setClientSelected}
setClients={setClients}
/>
<Managers managers={managers} setManagers={setManagers} setManager={setManager} />
<label htmlFor="start_date">Start date:</label>
<Field
type="date"
id="start_date"
name="start_date"
min="1000-01-01"
max="3000-12-31"
/>
<label htmlFor="end_date">End date:</label>
<Field
type="date"
id="end_date"
name="end_date"
min="1000-01-01"
max="3000-12-31"
/>
{errors.end_date && <p>{errors.end_date}</p>}
<label htmlFor="teams_link">Teams link:</label>
<Field
type="text"
id="teams_link"
name="teams_link"
placeholder="Your teams link"
/>
<label htmlFor="number_sprints">Number of sprints:</label>
<Field type="number" id="number_sprints" name="number_sprints" />
<label htmlFor="duration_sprints">
Duration of sprints (in weeks):
</label>
<Field
type="number"
id="duration_sprints"
name="duration_sprints"
/>
<label htmlFor="description">Description:</label>
<Field
type="text"
id="description"
name="description"
placeholder="Short description of the project, what was sold, what are the expectations of the client?"
rows="4"
cols="50"
/>
<label htmlFor="time_budget_type">Budget type:</label>
<Field name="time_budget_type" id="time_budget_type" as="select">
<option value="">--Please choose an option--</option>
<option value="Time and material">Time and material</option>
<option value="Time Package">Time Package</option>
</Field>
<label htmlFor="project_type">Project type:</label>
<Field name="project_type" id="project_type" as="select">
<option value="">--Please choose an option--</option>
<option value="Development">Development</option>
<option value="Design">Design</option>
<option value="Cybersecurity">Cybersecurity</option>
<option value="Cloud">Cloud</option>
</Field>
<label htmlFor="signed">Is the contract signed?</label>
<Field name="signed" id="signed" as="select">
<option value="">--Please choose an option--</option>
<option value="0">False</option>
<option value="1">True</option>
</Field>
<label htmlFor="chance_winning">
Chance to win the contract (max 100%):
</label>
<Field
type="number"
id="chance_winning"
name="chance_winning"
min="0"
max="100"
/>
<Tools
tools={tools}
toolsSelected={toolsSelected}
newTool={newTool}
setNewTool={setNewTool}
setToolsSelected={setToolsSelected}
setTools={setTools}
/>
<Technos
technos={technos}
technosSelected={technosSelected}
newTechno={newTechno}
setNewTechno={setNewTechno}
setTechnosSelected={setTechnosSelected}
setTechnos={setTechnos}
/>
<Collaborators
collaborators={collaborators}
setCollaborators={setCollaborators}
collaboratorsSelected={collaboratorsSelected}
setCollaboratorsSelected={setCollaboratorsSelected}
/>
<button type="submit">
Create
</button>
</Form>
)}
</Formik>
</div>
);
};
export default NewProject;```

Related

How to update existing data from DB with react-hook-form

I'm creating an E-commerce like website, we upload products through the admin site.
I have Upload.tsx file like so:
...
import FormDetails from '#components/FormDetails';
const Upload: React.FC = () => {
const { register, handleSubmit, reset, unregister, watch, setValue } =
useForm<UploadFormState>();
const watchPacksOrWholesale = watch('packsOrWholesale');
const [uploadLoading, setUploadLoading] = useState<boolean>(false);
const [file, setFile] = useState<File | null>(null);
const [url, setUrl] = useState<string>('');
const [progress, setProgress] = useState<number>(0);
const [createdAt, setCreatedAt] = useState<Timestamp | null>(null);
const saveProduct = useProductStore((state) => state.saveProduct);
const isLoading = useProductStore((state) => state.isLoadingSave);
const handleProductSubmit: SubmitHandler<UploadFormState> = (data) => {
const newProductUpload = {
drinkName: data.drinkName,
description: data.description,
category: data.category,
price: Number(data.price),
url,
createdAt,
packsOrWholesale: data.packsOrWholesale,
packSize: data.packSize,
};
saveProduct(newProductUpload, reset, setFile);
};
return (
<>
<Text fontSize="xl" mb={5} fontWeight="semibold">
Upload Drinks
</Text>
<form onSubmit={handleSubmit(handleProductSubmit)}>
<FormDetails
register={register}
file={file}
progress={progress}
setFile={setFile}
setProgress={setProgress}
isRequired={true}
unregister={unregister}
watchPacksOrWholesale={watchPacksOrWholesale}
setValue={setValue}
/>
<ButtonGroup mt={4} spacing={2}>
<Button
size="sm"
isLoading={uploadLoading}
isDisabled={!file || progress === 100}
onClick={handleProductImageUpload}
>
Upload Image
</Button>
<Button
size="sm"
type="submit"
variant="solid"
// isDisabled={!file || progress === 0}
colorScheme="success"
isLoading={isLoading}
>
Save Product
</Button>
</ButtonGroup>
</form>
</>
);
};
export default Upload;
.....
FormDetails.tsx
import {
....
} from '#chakra-ui/react';
import { FileInput } from '#components/FormFields';
import { drinkCategoriesArray } from '#data/index';
import { FormDetailsProps } from '#interfaces/index';
import { useEffect, useState } from 'react';
import { Controller } from 'react-hook-form';
const FormDetails: React.FC<FormDetailsProps> = ({
....
}) => {
const [isPacksOrWholesale, setIsPacksOrWholesale] = useState<boolean>(
product?.packsOrWholesale || false
);
useEffect(() => {
if (watchPacksOrWholesale) {
register('packsOrWholesale', { required: true });
} else {
unregister('packsOrWholesale');
}
}, [register, unregister, watchPacksOrWholesale, isPacksOrWholesale]);
return (
<>
<SimpleGrid columns={{ base: 1, sm: 2 }} spacing={6} textAlign="left">
<FormControl>
<FormLabel fontSize="14px">Drink name</FormLabel>
<Input
{...register('drinkName', { required: isRequired })}
type={'text'}
placeholder="Meridian"
defaultValue={(showValue && product?.drinkName) || ''}
/>
</FormControl>
<FormControl>
<FormLabel fontSize="14px">Description</FormLabel>
<Input
{...register('description', { required: isRequired })}
type={'text'}
placeholder="Meridian wine is the best"
defaultValue={(showValue && product?.description) || ''}
/>
</FormControl>
<FormControl>
<FormLabel fontSize="14px">Select Category</FormLabel>
<Select
{...register('category', { required: isRequired })}
placeholder="Select category"
defaultValue={(showValue && product?.category) || ''}
>
{drinkCategoriesArray.map(({ drinkCategory, drink_id }) => (
<option key={drink_id} value={drinkCategory}>
{drinkCategory}
</option>
))}
</Select>
</FormControl>
<FormControl>
<FormLabel>
<FormLabel fontSize="14px">Select Product method</FormLabel>
<HStack>
<Text fontWeight={'semibold'}>Product in wholesale</Text>
<Switch
id="packs switch"
{...register('packsOrWholesale')}
colorScheme={'primary'}
size={'sm'}
fontWeight={'semibold'}
defaultChecked={showValue && product?.packsOrWholesale}
isChecked={isPacksOrWholesale}
onChange={() => setIsPacksOrWholesale(!isPacksOrWholesale)}
/>
<Text fontWeight={'semibold'}>Product in packs</Text>
</HStack>
</FormLabel>
</FormControl>
{isPacksOrWholesale ? (
<FormControl>
<FormLabel>
<FormLabel fontSize="14px">Select pack size</FormLabel>
<Select
{...register('packSize', {
required: true,
})}
placeholder="Select Pack Size"
defaultValue={(showValue && product?.packSize) || ''}
>
<option value="6 Packs">6 Packs</option>
<option value="12 Packs">12 Packs</option>
</Select>
</FormLabel>
</FormControl>
) : null}
<FormControl mt={'3px'}>
<FormLabel fontSize="14px">Price</FormLabel>
<Input
{...register('price', { required: isRequired })}
type="number"
placeholder="500 (in Dollars)"
defaultValue={(showValue && product?.price) || ''}
/>
</FormControl>
</SimpleGrid>
....
</Box>
</>
);
};
export default FormDetails;
.....
ProductEdit.tsx
import FormDetails from '#components/FormDetails';
import { useProductStore } from '#store/useProductStore';
const ProductEdit = forwardRef(({ product }: ProductType, ref) => {
const { id } = useParams<ProductParams>();
const { register, handleSubmit, watch, unregister, reset, setValue } =
useForm<UploadFormState>();
const watchPacksOrWholesale = watch('packsOrWholesale');
const [file, setFile] = useState<File | null>(null);
const [uploadLoading, setUploadLoading] = useState<boolean>(false);
const [url, setUrl] = useState<string>('');
const [progress, setProgress] = useState<number>(0);
const { isOpen, onOpen, onClose } = useDisclosure();
const editProduct = useProductStore((state) => state.editProduct);
const isLoading = useProductStore((state) => state.isLoadingEdit);
const handleProductUpdate: SubmitHandler<UploadFormState> = (data) => {
const newUpdate = {
drinkName: data.drinkName || product?.drinkName,
description: data.description || product?.description,
category: data.category || product?.category,
url: url || product?.url,
price: Number(data.price) || Number(product?.price),
packsOrWholesale: data.packsOrWholesale || product?.packsOrWholesale,
packSize: data.packSize || product?.packSize,
};
// editProduct(id, newUpdate, onClose, setFile);
console.log(newUpdate);
};
return (
<Box>
<IconButton
aria-label="edit-button"
size="sm"
colorScheme="secondary"
variant="ghost"
onClick={onOpen}
icon={<RiPencilLine size="18px" />}
/>
<Modal
isCentered
size="lg"
onClose={onClose}
isOpen={isOpen}
motionPreset="slideInBottom"
closeOnOverlayClick={false}
>
<ModalOverlay backdropFilter={'blur(5px)'} />
<ModalContent>
<ModalHeader>
Edit Product{' '}
<chakra.span color="success.700">{product?.drinkName}</chakra.span>?
</ModalHeader>
<ModalCloseButton size="sm" />
<form onSubmit={handleSubmit(handleProductUpdate)}>
<ModalBody>
<FormDetails
register={register}
file={file}
progress={progress}
setFile={setFile}
setProgress={setProgress}
showValue={true}
isRequired={false}
product={product}
unregister={unregister}
watchPacksOrWholesale={watchPacksOrWholesale}
setValue={setValue}
/>
</ModalBody>
<ModalFooter>
<ButtonGroup mt={4} spacing={2}>
<Button
isLoading={uploadLoading}
onClick={handleProductImageUpload}
isDisabled={!file || progress === 100}
size="sm"
>
Upload Image
</Button>
<Button
size="sm"
type="submit"
variant="solid"
isLoading={isLoading}
colorScheme="success"
>
Update Product
</Button>
</ButtonGroup>
</ModalFooter>
</form>
</ModalContent>
</Modal>
</Box>
);
});
export default ProductEdit;
I have a reusable component, called FormDetails. I use this component to get the data from the admin input and do an upload. Alternatively, I also use it for the ProductEdit component.
Everything works fine, except for the fact that,
in the FormDetails component, I want to use react-hook-form to manage the state of what renders. i.e if watchPacksOrWholesale is true, which is linked to the Switch component, the Select component which holds the packSize should show.
The ProductEdit component picks the existing Product from firebase and then on editing the product using react-hook-form if I disable the Switch component the value for the PackSize should be an empty string, meaning I don't want a packSize. I should be able to update the product on Firebase.
All I'm saying is that the Switch component does not update well.

Submitting dynamically created form to database in reactJS

I am trying to insert the values through this dynamic form but I am only able to insert just one entry (even though I enter multiple forms) and the values that are getting inserted are 0 (in the database as well).
I am passing the index still I am not getting the desired output. The list looks like this:
List of the entered details
The previous entry was entered manually.
This is the CODE through which I submit values.
import React, { useEffect, useState } from "react";
import './roominfo.css';
import { ImBin } from 'react-icons/im';
import { useNavigate, useParams } from 'react-router-dom'
import RoomInfoService from '../services/RoomInfoService';
const RoomInfo = () => {
const [shared_no] = useState();
const [no_of_rooms] = useState();
const [rent_per_month] = useState();
const [vacancy] = useState();
const [total_capacity] = useState();
const [pg_fid, setpgfid] = useState();H
let handleChange = (i, e) => {
let newFormValues = [...formValues]; /** makes a copy of the current form values and assign it to newFormValues **/
newFormValues[i][e.target.name] = e.target.value;
setFormValues(newFormValues);
}
const navigate = useNavigate();
let addFormFields = () => {
setFormValues([...formValues, { shared_no: "", no_of_rooms: "", rent_per_month: "", vacancy: "",total_capacity: ""}])
};
let removeFormFields = (i) => {
let newFormValues = [...formValues];
newFormValues.splice(i, 1);
setFormValues(newFormValues)
}
const [formValues, setFormValues] = useState([{ shared_no: "", no_of_rooms: "", rent_per_month: "", total_capacity: "", vacancy: "" }])
/*const [settotal_no_of_rooms] = useState('1');
const [setrooms_entered] = useState('1');
const getTotalCapacity = () => {
return formValues.reduce((total,element) => {
return total + Number(element.shared_no) * Number(element.no_of_rooms);
}, 0);
};*/
const resetForm = () => setFormValues([{shared_no: "", no_of_rooms: "", rent_per_month: "", vacancy: "",total_capacity: ""}]);
const Total_no_of_rooms_entered = () => {
return formValues.reduce((total, element) => {
return total + Number(element.no_of_rooms);
}, 0);
};
const saveRoomInfo = (e) =>{
e.preventDefault();
const roominfo = [...formValues];
RoomInfoService.createRoomInfo(roominfo).then((response) => {
console.log(response.data);
navigate('/listroominfo');
}).catch(error =>{
console.log(error);
})
}
return (
<>
<div className='heading'>
<h1 style={{ marginBottom: '50px' }}>Enter Your PG Room Details</h1>
</div>
<form>
<div className="form-inline" style={{ marginBottom: '50px' }}>
{/*<label>Total of Rooms: </label>
<input type="number" name="total_no_of_rooms" style={{ width: '90px' }} onClick={(e) => settotal_no_of_rooms(e.target.value)} defaultValue={1} /> */}
<label>Rooms Entered:</label>
<div><input type="number" name="rooms_entered" value={Total_no_of_rooms_entered()} readOnly></input></div>
</div>
</form>
<div>
<form>
{formValues.map((element, index) => (
<div className="form-inline" key={index} style={{ borderBottom: '1px solid black' }}>
<label>Sharing Number</label>
<select id="shared_no" name="shared_no" title="no of shared rooms" value={element.shared_no || ""} onChange={e => handleChange(index, e)} >
<option value="">---Select---</option>
<option value="1">Single Sharing</option>
<option value="2">Two Bed Sharing</option>
<option value="3">Three Bed Sharing</option>
<option value="4">Four Bed Sharing</option>
<option value="5">Five Bed Sharing</option>
</select>
<label> No of Rooms:</label>
<input type="number" style={{ width: '60px' }} name="no_of_rooms" value={element.no_of_rooms || ""} onChange={e => handleChange(index, e)}></input>
<label> Rent per month:</label>
<input type="number" style={{ width: '100px' }} name="rent_per_month" value={element.rent_per_month || ""} onChange={e => handleChange(index, e)}></input>
<label> Total Capacity:</label>
<input type="number" style={{ width: '80px' }} name="total_capacity" value={element.total_capacity} onChange={e => handleChange(index, e)}></input>
<label> Vacancy:</label>
<input type="number" style={{ width: '50px' }} name="vacancy" value={element.vacancy || ""} onChange={e => handleChange(index, e)}></input>
{
index ?
<button type="button" className="button-remove" onClick={() => removeFormFields(index)}><ImBin size={20}/></button>
: null
}
</div>
))}
<div className="button-section">
<button className="button" type="button" onClick={() => addFormFields()}>Add</button>
<button className="button" type="button" onClick={resetForm}>Reset</button>
<button className="button" type="submit" onClick={(e) => saveRoomInfo(e)}>Submit</button>
</div>
</form>
</div>
</>
)
}
export default RoomInfo
Reference of Dynamic Form
The issue is while I am fetching the data from the form. But I can't figure it out. Thank you.

Disable submit button in React functional component if all input fields are not completed

I am new in React world, I have done a lot of research but I still have not found answer to my issue
which is similar with How to conditionally disable the submit button with react-hook-form?
The problem is that my Submit button gets active if I am completing just 1 input field and I don't get why the expression {user.length < 1 && pass.length < 1 && email.length < 1 && app.length < 1} evaluate true even its not.
import React, { useState } from "react";
import TextField from "#material-ui/core/TextField";
import MenuItem from "#material-ui/core/MenuItem";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import { appList, apisList } from "./appList";
const useStyles = makeStyles(theme => ({
root: {
"& .MuiTextField-root": {
margin: theme.spacing(0.7),
width: "30ch"
}
}
}));
const submitButtonStyle = {
marginLeft: "7px",
marginTop: "15px",
marginBottom: "15px"
};
function FormPage() {
const classes = useStyles();
// const formState = { user: '',pass: '', email: '', app: ''};
// const [login, setLogin] = useState(formState);
// const { user, pass, email, app, } = login;
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
const [email, setEmail] = useState("");
const [app, setApp] = useState("");
const handleUser = e => {
setUser(e.target.value);
console.log(user);
};
const handlePass = e => {
setPass(e.target.value);
};
const handleEmail = e => {
setEmail(e.target.value);
};
const handleApp = e => {
setApp(e.target.value);
};
const handleSubmit = e => {
e.preventDefault();
const login = { user: user, pass: pass, email: email, app: app };
console.log(login);
};
return (
<div>
<form
className={classes.root}
noValidate
autoComplete="off"
onSubmit={handleSubmit}
>
<div>
<TextField
required
name="user"
id="outlined-helperText"
label="Integration Username"
onChange={e => {
handleUser(e);
}}
value={user}
variant="outlined"
/>
<TextField
required
name="pass"
id="outlined-password-input"
label="Integration Password"
type="password"
onChange={e => handlePass(e)}
value={pass}
autoComplete="current-password"
variant="outlined"
/>
<TextField
required
name="email"
id="outlined-helperText"
label="Email"
value={email}
onChange={e => handleEmail(e)}
variant="outlined"
/>
<TextField
required
name="app"
select
label="Select app to redirect"
value={app}
onChange={e => handleApp(e)}
helperText=""
variant="outlined"
>
{appList.map(app => (
<MenuItem key={app.value} value={app.value}>
{app.label}
</MenuItem>
))}
</TextField>
</div>
</form>
<Button
style={submitButtonStyle}
variant="contained"
color="primary"
onClick={handleSubmit}
disabled={
user.length < 1 &&
pass.length < 1 &&
email.length < 1 &&
app.length < 1
}
>
Submit
</Button>
</div>
);
}
export default FormPage;
Your expression is actually wrong. It should be:
disabled={
user.length < 1 ||
pass.length < 1 ||
email.length < 1 ||
app.length < 1
}
When any one is correct, then it should be disabled. So keep it in || comparison. In simple words, what you're checking is for error is true. Then, you're supposed to see:
If user length is less than 1
OR pass length is less than 1
OR email length is less than 1
OR app length is less than 1
If any one of those above conditions are true, then it's wrong. So you have to use || and not &&! Full code below:
import React, { useState } from "react";
import TextField from "#material-ui/core/TextField";
import MenuItem from "#material-ui/core/MenuItem";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import { appList, apisList } from "./appList";
const useStyles = makeStyles(theme => ({
root: {
"& .MuiTextField-root": {
margin: theme.spacing(0.7),
width: "30ch"
}
}
}));
const submitButtonStyle = {
marginLeft: "7px",
marginTop: "15px",
marginBottom: "15px"
};
function FormPage() {
const classes = useStyles();
// const formState = { user: '',pass: '', email: '', app: ''};
// const [login, setLogin] = useState(formState);
// const { user, pass, email, app, } = login;
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
const [email, setEmail] = useState("");
const [app, setApp] = useState("");
const handleUser = e => {
setUser(e.target.value);
console.log(user);
};
const handlePass = e => {
setPass(e.target.value);
};
const handleEmail = e => {
setEmail(e.target.value);
};
const handleApp = e => {
setApp(e.target.value);
};
const handleSubmit = e => {
e.preventDefault();
const login = { user: user, pass: pass, email: email, app: app };
console.log(login);
};
return (
<div>
<form
className={classes.root}
noValidate
autoComplete="off"
onSubmit={handleSubmit}
>
<div>
<TextField
required
name="user"
id="outlined-helperText"
label="Integration Username"
onChange={e => {
handleUser(e);
}}
value={user}
variant="outlined"
/>
<TextField
required
name="pass"
id="outlined-password-input"
label="Integration Password"
type="password"
onChange={e => handlePass(e)}
value={pass}
autoComplete="current-password"
variant="outlined"
/>
<TextField
required
name="email"
id="outlined-helperText"
label="Email"
value={email}
onChange={e => handleEmail(e)}
variant="outlined"
/>
<TextField
required
name="app"
select
label="Select app to redirect"
value={app}
onChange={e => handleApp(e)}
helperText=""
variant="outlined"
>
{appList.map(app => (
<MenuItem key={app.value} value={app.value}>
{app.label}
</MenuItem>
))}
</TextField>
</div>
</form>
<Button
style={submitButtonStyle}
variant="contained"
color="primary"
onClick={handleSubmit}
disabled={
user.length < 1 ||
pass.length < 1 ||
email.length < 1 ||
app.length < 1
}
>
Submit
</Button>
</div>
);
}
export default FormPage;

Formik validation on each mui stepper

I am using Material UI stepper and on each step I have different components. I am trying to validate each step using Yup and have used Formik but the instead of validating it moves on to next step. How can I validate this on every step and can get all the data of each step at last.
import React from 'react';
..
import FormStep1 from './FormStep1';
import FormStep2 from './FormStep2';
function getSteps() {
return ['Select general campaign settings', 'Ads setting', 'Upload Ad contents', 'Review and Submit'];
}
function getStepContent(stepIndex, handleStepSubmit, handleNext) {
switch (stepIndex) {
case 0:
return <FormStep1 onSubmit={handleStepSubmit} onNext={handleNext}/>;
case 1:
return <FormStep2 />;
case 2:
return 'No need to worry abt this!';
default:
return 'Unknown stepIndex';
}
}
const IobdCampCreate = () => {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const [state, setState] = React.useState({
steps: [
{ name: 'Select general campaign settings', data: {} },
{ name: 'form2', data: {} }
],
activeStep: 0,
});
const handleNext = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleReset = () => {
setActiveStep(0);
};
const handleStepSubmit = (stepIndex, data) => {
console.log("-------", stepIndex, data)
setState((prevState) => ({
...prevState,
activeStep: prevState.activeStep + 1,
steps: prevState.steps.map((step, index) => {
if (stepIndex !== index) {
return step;
}
return {
...step,
data
}
})
}))
}
return (
<React.Fragment>
<div className={classes.root}>
<Stepper activeStep={activeStep} alternativeLabel>
{state.steps.map((label) => (
<Step key={label.name}>
<StepLabel>{label.name}</StepLabel>
</Step>
))}
</Stepper>
<div>
{activeStep === state.steps.length ? (
<div>
<Typography className={classes.instructions}>All steps completed</Typography>
<Button onClick={handleReset}>Reset</Button>
</div>
) : (
<div>
<div className={classes.instructions}>
{getStepContent(activeStep, handleStepSubmit,handleNext )}
</div>
<div>
<Button
disabled={activeStep === 0}
onClick={handleBack}
className={classes.backButton}
>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext}>
{activeStep === state.steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
)}
</div>
</div>
</React.Fragment>
);
};
export default IobdCampCreate;
Here is a the formstep1.js
import React from 'react';
import { Formik } from 'formik';
const FormStep1 = (props) => (
<div>
<h3>Form A</h3>
<Formik
initialValues={{ email: '', password: '' }}
validate={values => {
const errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
return errors;
}}
onSubmit={(values) => {
props.onSubmit(0, values);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
/* and other goodies */
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && errors.email}
<input
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{errors.password && touched.password && errors.password}
<button variant="contained" color="primary" type="submit" >
Next
</button>
</form>
)}
</Formik>
</div>
);
export default FormStep1;

Page not re-rendering after hook state update

The page renders the input correctly, however errors only seem to appear on the second render even though they are displayed is useForm state on the first render.
So for the password field I enter a single character and the state of useForm changes to {password: "Must be at.....} but the screen does not update to display errors.password until I enter in another character.
// nodejs library that concatenates classes
// reactstrap components
import {
Button,
Card,
CardBody,
CardHeader,
Col,
Container,
Form,
FormFeedback,
FormGroup,
Input,
Row
} from "reactstrap";
import {register} from "apiCalls/AuthRequests";
import useForm from "hooks/AuthHooks";
import {registerFormValidation} from "components/validation/AuthFormValidation";
function RegisterForm(props) {
const [loading, setLoading] = useState(false);
const {values, handleChange, handleSubmit, errors, setErrors} = useForm(submit, registerFormValidation);
const emailRef = useRef(null);
const passwordRef = useRef(null);
const accessCodeRef = useRef(null);
async function submit() {
setLoading(true);
const response = await register(values.email, values.password, values.accessCode);
if (response.ok) {
} else if (response.status === 422) {
if ("access_code" in response.data) {
accessCodeRef.current.focus();
setErrors({accessCode: response.data.access_code});
}
if ("email" in response.data) {
setErrors({email: response.data.email});
emailRef.current.focus();
}
if ("password" in response.data) {
setErrors({password: response.data.password});
passwordRef.current.focus();
}
}
setLoading(false)
}
useEffect(() => {
console.log(errors);
});
return (
<>
<div className="content">
<Container className="pb-5">
<Row>
<Col lg="6" md="8" className="ml-auto mr-auto">
<Card className="bg-secondary border-0">
<CardHeader className="bg-transparent">
<div className="text-center">
<h2>Register</h2>
</div>
</CardHeader>
<CardBody className="px-lg-5 py-lg-5">
<Form role="form" onSubmit={handleSubmit}>
<FormGroup>
<label
className="form-control-label"
>
Email
</label>
<Input
name="email"
type="email"
innerRef={emailRef}
value={values.email || ""}
onChange={handleChange}
invalid={!!errors.email}
required
/>
<FormFeedback>{errors.email}</FormFeedback>
</FormGroup>
<FormGroup>
<label
className="form-control-label"
>
Password
</label>
<Input
name="password"
type="password"
innerRef={passwordRef}
value={values.password || ""}
onChange={handleChange}
invalid={!!errors.password}
required
/>
<FormFeedback>{errors.password}</FormFeedback>
</FormGroup>
<FormGroup>
<label
className="form-control-label"
>
Access Code
</label>
<Input
name="accessCode"
type="text"
innerRef={accessCodeRef}
value={values.accessCode || ''}
onChange={handleChange}
invalid={!!errors.accessCode}
required
/>
<FormFeedback>{errors.accessCode}</FormFeedback>
</FormGroup>
<Row className="my-4">
<Col xs="12">
<div
className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id="customCheckRegister"
type="checkbox"
required
/>
<label
className="custom-control-label"
htmlFor="customCheckRegister"
>
<span className="text-muted">
I agree with the{" "}
<a
href=""
target="_blank"
rel="noopener noreferrer"
>
Privacy Policy
</a>
</span>
</label>
</div>
</Col>
</Row>
<div className="text-center">
<Button disabled={loading} className="mt-4" color="info" type="submit">
Create account
</Button>
</div>
</Form>
</CardBody>
</Card>
</Col>
<Col md="4" className="ml-auto mr-auto">
<h2>Being a photographer is easier with <b className="text-primary">FOCAL</b></h2>
<ul>
<li>
<h4>Focal is great</h4>
</li>
<li>
<h4>Save time</h4>
</li>
<li>
<h4>More customers</h4>
</li>
</ul>
</Col>
</Row>
</Container>
</div>
</>
);
}
export default RegisterForm;
const useForm = (callback, validate) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = (event) => {
if (event) event.preventDefault();
setIsSubmitting(true);
};
useEffect(() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
setIsSubmitting(false);
}
}, [callback, errors, isSubmitting]);
useEffect(() => {
setErrors(validate(values, errors));
}, [validate, values, errors]);
const handleChange = (event) => {
event.persist();
setValues(values => ({...values, [event.target.name]: event.target.value}));
setErrors(validate(values, errors));
};
return {
handleChange,
handleSubmit,
setErrors,
values,
errors
}
};
export default useForm;
export function registerFormValidation(values, errors) {
if (values.email === ""){
delete errors.email;
}
if (values.password) {
if (!verifyLength(values.password, PASSWORD_LENGTH)){
errors.password = "Password must be greater than 8 Characters";
} else {
delete errors.password;
}
}
if (values.accessCode === "") {
delete values.accessCode;
}
return errors
}```
I can appreciate that it's of interest to work on a custom hook but forms are ubiquitous and there are solid, proven means to work with them. IMHO Formik is probably the best.
With the three fields that you have there, you could implement something like the following:
import React from 'react';
import { Formik } from 'formik';
const BasicExample = () => (
<div>
<Formik
initialValues={{ email: '', password: '', accessCode: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={props => (
<form onSubmit={props.handleSubmit}>
<input
type="email"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
/>
{props.errors.email && <div id="feedback">{props.errors.email}</div>}
<input
type="password"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.password}
name="password"
/>
{props.errors.password && <div id="feedback">{props.errors.password}</div>}
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.accessCode}
name="accessCode"
/>
{props.errors.accessCode && <div id="feedback">{props.errors.acessCode}</div>}
<button type="submit">Submit</button>
</form>
)}
/>
</div>
);
Note that the above is just from memory - which to some extent speaks to it's simplicity. As initialValues are supplied to Formik, you can build complex component hierarchies without having to pass down form state but that's just one of the various benefits.
I have an assumption.
In registerFormValidation doesn't modify errors object. Create a new each time.
FIXED:
const useForm = (callback, validate) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const handleSubmit = (event) => {
if (event) event.preventDefault();
if (Object.keys(errors).length === 0){
callback();
}
};
const handleChange = (event) => {
event.persist();
setValues(values => ({...values, [event.target.name]: event.target.value}));
setErrors(validate({[event.target.name]: event.target.value}, errors));
};
return {
handleChange,
handleSubmit,
setErrors,
values,
errors
}
};
export default useForm;
got rid of useEffect!

Resources