Probleme with Reach-hook-form validation on update - reactjs

I Working with hook-form and MaterialUI, I want to make the validation of the form, the validation working well in adding, but on update (input are ready full) in shows me erros (inputs are empty):
my code:
import Box from "#mui/material/Box";
import Button from "#mui/material/Button";
import Grid from "#mui/material/Grid";
import TextField from "#mui/material/TextField";
import axios from "axios";
import "bootstrap/dist/css/bootstrap.min.css";
import React from "react";
import { useForm } from "react-hook-form";
import { useNavigate, useParams } from "react-router-dom";
function UpdateEmployer(props) {
const { id } = useParams();
const { register, handleSubmit, formState, control } = useForm({
mode: "onChange",
});
// declare states
const [FullName, setFullName] = React.useState("");
const [cin, setCin] = React.useState("");
const [profile, setProfile] = React.useState("");
const [years, setYears] = React.useState("");
const { isSubmitting, errors, isValid } = formState;
const navigate = useNavigate();
// get data from form and post to api
const onSubmit = async (data) => {
// set state from data
const { FullName, cin, profile, years } = data;
console.log(data);
await axios.put(`http://localhost:5000/employers/${id}`, {
fullname: FullName,
cin: cin,
profile: profile,
experience: years,
});
navigate("/");
};
React.useEffect(() => {
axios.get(`http://localhost:5000/employers/${id}`).then((res) => {
setFullName(res.data.fullname);
setCin(res.data.cin);
setProfile(res.data.profile);
setYears(res.data.experience);
});
}, []);
// -------------------- render componenet --------------------
return (
<div className="container mt-4">
<div className="text-center my-3 font-weight-bold">Update Employer</div>
<form onSubmit={handleSubmit(onSubmit)} style={{ width: "100%" }}>
<Grid container spacing={2}>
<Grid item xs={8}>
<TextField
id="FullName"
name="FullName"
label="Full Name"
variant="outlined"
fullWidth
value={FullName}
{...register("FullName", { required: "Please Entre your name" })}
onChange={(e) => setFullName(e.target.value)}
/>
<span className="text-danger">
{errors.FullName && errors.FullName.message}
</span>
</Grid>
<Grid item xs={4}>
<TextField
id="cin"
name="cin"
label="CIN"
variant="outlined"
value={cin}
fullWidth
{...register(
"cin",
{ required: "Please Entre your cin" },
{
minLength: {
value: 6,
message: "Oops! the cin have to ve at least 6 characters",
},
}
)}
onChange={(e) => {
console.log(e.target.value);
setCin(e.target.value);
}}
/>
<span className="text-danger">
{errors.cin && errors.cin.message}
</span>
</Grid>
<Grid item xs={8}>
<TextField
id="profile"
name="profile"
label="Profile"
variant="outlined"
fullWidth
value={profile}
{...register("profile", {
required: "Please Entre your profile",
})}
onChange={(e) => setProfile(e.target.value)}
/>
</Grid>
<Grid item xs={4}>
<TextField
id="years"
name="years"
label="Years of Experience"
variant="outlined"
value={years}
fullWidth
{...register("years", { required: "Please Entre your years" })}
onChange={(e) => setYears(e.target.value)}
/>
</Grid>
<Grid item xs={12}>
<Box textAlign="center">
<Button
id="Update_employer"
variant="contained"
color="primary"
type="submit"
// disabled={!isValid}
>
Update Employee
</Button>
</Box>
</Grid>
</Grid>
</form>
</div>
);
}
export default UpdateEmployer;
The fill he inputs in with useEffect() and it shows me that the input are full with the right information but when I want to submit it shows me that error that the input are empty.

Related

FormData: The data is not placed in the formData, and an empty object is printed

I have a project in order to run a contracting, building and construction company, and I must create an invoice, and to create an invoice I must send a request to the backend, and the form of the request must be as shown in this picture:
And this is what the interface should look like:
The problem is that when I print "formData" it prints an empty object, even though I passed the data to "formData", how can I solve the problem?
this is the empty Object:
And the data is sent to the backend empty
and when i print:
console.log(formData.get("invoice"));
console.log(formData.get("data"));
the console was:
null
[object Object]
file.js:
import React, { useState } from "react";
import InputAdornment from "#material-ui/core/InputAdornment";
import TextField from "#material-ui/core/TextField";
import Grid from "#material-ui/core/Grid";
import "date-fns";
import DateFnsUtils from "#date-io/date-fns";
// import {
// KeyboardDatePicker,
// MuiPickersUtilsProvider,
// DatePicker,
// } from "#material-ui/pickers";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import CloudUploadIcon from "#material-ui/icons/CloudUpload";
import { addInvoice } from "../../../store/invoiceSlice";
import Icon from "#material-ui/core/Icon";
import { motion } from "framer-motion";
import { useDispatch } from "react-redux";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { alpha } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1),
},
},
input: {
display: "none",
},
button: {
margin: theme.spacing(1),
// padding: theme.spacing(4),
},
}));
function ShippingTab(props) {
const dispatch = useDispatch();
const classes = useStyles();
const [issueDate, setIssueDate] = useState(new Date());
const [dueDate, setDueDate] = useState(new Date());
const [netAmount, setNetAmount] = useState("");
const [taxNumber, setTaxNumber] = useState("");
const [grossAmount, setGrossAmount] = useState("");
const [file, setFile] = useState(null);
let formData = new FormData();
const fileSelectedHandler = (event) => {
console.log(event.target.files[0]);
const file = event.target.files[0];
if (event.target && file) {
formData.append("invoice", file);
setFile(file);
}
};
const uploadHandler = (event) => {
// fd.append("invoice", files);
formData.append("data", {
grossAmount,
taxNumber,
netAmount,
dueDate,
issueDate,
});
console.log("formmmmmmmmmmm Data: ", formData);
// call api
dispatch(addInvoice(formData));
};
const handleissueDateChange = (date) => {
setIssueDate(date);
console.log("date issssssssss: ", date);
console.log("date issssssssss: ", issueDate);
};
const handleDueDateChange = (date) => {
setDueDate(date);
};
const handleNetAmountChange = (event) => {
setNetAmount(event.target.value);
};
const handleTaxAmountChange = (event) => {
setTaxNumber(event.target.value);
};
const handleGrossAmountChange = (event) => {
setGrossAmount(event.target.value);
};
return (
<>
{/* <MuiPickersUtilsProvider utils={DateFnsUtils}>
<div className="flex -mx-4">
<KeyboardDatePicker
inputVariant="outlined"
className="mt-8 mb-16"
margin="normal"
id="date-picker-dialog"
label="issue Date"
format="MM/dd/yyyy"
KeyboardButtonProps={{
"aria-label": "change date",
}}
value={issueDate}
onChange={handleissueDateChange}
/> */}
<div className="flex -mx-4">
<TextField
id="date"
variant="outlined"
label="Choose Issue Date"
className="mt-8 mb-16 mr-5"
type="date"
defaultValue={issueDate}
InputLabelProps={{
shrink: true,
}}
// value={issueDate}
onChange={handleissueDateChange}
fullWidth
/>
<TextField
id="date"
variant="outlined"
className="mt-8 mb-16"
label="Choose Due Date"
type="date"
defaultValue={dueDate}
InputLabelProps={{
shrink: true,
}}
// value={dueDate}
onChange={handleDueDateChange}
fullWidth
/>
</div>
{/* <KeyboardDatePicker
inputVariant="outlined"
className="mt-8 mb-16 ml-6"
margin="normal"
id="date-picker-dialog"
label="Due Date"
format="MM/dd/yyyy"
KeyboardButtonProps={{
"aria-label": "change date",
}}
value={dueDate}
onChange={handleDueDateChange}
/>
</div>
</MuiPickersUtilsProvider> */}
<TextField
className="mt-8 mb-16"
label="Net Amount"
id="extraShippingFee"
variant="outlined"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
}}
value={netAmount}
onChange={handleNetAmountChange}
fullWidth
/>
<TextField
className="mt-8 mb-16"
label="Tax Number"
id="extraShippingFee"
variant="outlined"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
}}
value={taxNumber}
onChange={handleTaxAmountChange}
fullWidth
/>
<TextField
className="mt-8 mb-16"
label="Gross Amount"
id="extraShippingFee"
variant="outlined"
InputProps={{
startAdornment: <InputAdornment position="start">$</InputAdornment>,
}}
value={grossAmount}
onChange={handleGrossAmountChange}
fullWidth
/>
<div className={classes.root}>
<input
accept="application/pdf"
// className={classes.input}
// multiple
type="file"
onChange={fileSelectedHandler}
/>
{/* <label htmlFor="contained-button-file"> */}
{/* <Button
variant="contained"
color="primary"
size="large"
className={classes.button}
startIcon={<CloudUploadIcon />}
> */}
{/* Upload */}
{/* </Button> */}
{/* </label> */}
</div>
<motion.div
className="flex"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0, transition: { delay: 0.3 } }}
>
<Button
className="whitespace-nowrap mx-4"
variant="contained"
color="secondary"
// onClick={handleRemoveProduct}
startIcon={<Icon className="hidden sm:flex">delete</Icon>}
>
Cancel
</Button>
<Button
className="whitespace-nowrap mx-4"
variant="contained"
color="secondary"
// disabled={_.isEmpty(dirtyFields) || !isValid}
onClick={uploadHandler}
>
Create
</Button>
</motion.div>
</>
);
}
export default ShippingTab;
#hiba-youssef This is not a problem, console logging formData always gives empty obj but that doesn't mean it is empty, to print the values you can use get() method , kindly refer this official documenation https://developer.mozilla.org/en-US/docs/Web/API/FormData/get.
--EDIT--
As per your postman screenshot you can modify the uploadHandler fn as below
const uploadHandler = (event) => {
const formData=new FormData();
formData.append('grossAmount',grossAmount);
formData.append('taxNumber',taxNumber);
formData.append('dueDate',dueDate);
formData.append('netAmount',netAmount);
formData.append('issueDate',issueDate);
formData.append('issueDate',issueDate);
formData.append('invoice',file);
// now the formData is as per postman screenshot. You can also add all keys to an array and loop it
};
Thanks ,
working fine when i used => Object.fromEntries(formData.entries())
const uploadHandler = (event) => {
const formData=new FormData();
formData.append('grossAmount',grossAmount);
formData.append('taxNumber',taxNumber);
formData.append('dueDate',dueDate);
formData.append('netAmount',netAmount);
formData.append('issueDate',issueDate);
formData.append('issueDate',issueDate);
formData.append('invoice',file);
console.log(Object.fromEntries(formData.entries()))
};
if you send data in api using formData you must remember.
1- if data with text and image you need to use inside header
headers = {
"Content-Type": "multipart/form-data",
};
2- if data only text use
headers = {
"Content-Type": "application/json",
};

Material UI with Login Template with React.js

I'm brand new to Material UI. I am currently trying to implement it into my project. Unfortunately, it's not cooperating. I think I am putting in everything right, but because I have never used MUI before, I am not sure. Can someone please help me figure out where the error is? I have tried multiple times, putting in the code I had before I tried to implement MUI. Thank you!
I will include the code before I tried to implement MUI, and after. The first is before MUI implementation. The last block of code is my Auth.jsx file.
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
export default function Login({ updateToken, toggleView }) {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleUsername = (e) => setUsername(e.target.value);
const handlePassword = (e) => setPassword(e.target.value);
const history = useHistory();
const handleSubmit = async (e) => {
e.preventDefault();
try {
const fetchResults = await fetch(`http://localhost:3001/user/`, {
method: "POST",
body: JSON.stringify({ username, password }),
headers: new Headers({
"Content-Type": "application/json",
}),
});
const json = await fetchResults.json();
if (!json.user || !json.sessionToken) {
alert(json.message);
return;
}
updateToken(json.sessionToken);
history.push("/home");
} catch (err) {
console.error(err);
}
};
return (
<div>
<div className='signin'>
<form onSubmit={handleSubmit}>
<input
type='username'
onChange={handleUsername}
name='username'
placeholder='Username'
required
value={username}
className='signin__input'
/>
<input
type='password'
onChange={handlePassword}
name='password'
placeholder='Password'
type='password'
required
value={password}
className='signin__input'
/>
<button className='login__button' type='submit'>
Login
</button>
</form>{" "}
<p className='signin__toggle' onClick={() => history.push("./signup")}>
Don't have an account? Sign up here.{" "}
</p>
</div>
</div>
);
}
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import Avatar from "#mui/material/Avatar";
import Button from "#mui/material/Button";
import CssBaseline from "#mui/material/CssBaseline";
import TextField from "#mui/material/TextField";
import FormControlLabel from "#mui/material/FormControlLabel";
import Checkbox from "#mui/material/Checkbox";
import Link from "#mui/material/Link";
import Grid from "#mui/material/Grid";
import Box from "#mui/material/Box";
import LockOutlinedIcon from "#mui/icons-material/LockOutlined";
import Typography from "#mui/material/Typography";
import Container from "#mui/material/Container";
import { createTheme, ThemeProvider } from "#mui/material/styles";
function Copyright(props) {
return (
<Typography
variant='body2'
color='text.secondary'
align='center'
{...props}
>
{"Copyright © "}
<Link color='inherit' href='https://courtney-downs.web.app/'>
Courtney Downs Portfolio
</Link>{" "}
{new Date().getFullYear()}
{"."}
</Typography>
);
}
const theme = createTheme();
export default function Login2({ updateToken, toggleView }) {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleUsername = (e) => setUsername(e.target.value);
const handlePassword = (e) => setPassword(e.target.value);
const history = useHistory();
const handleSubmit = async (e) => {
e.preventDefault();
try {
const fetchResults = await fetch(`http://localhost:3001/user/`, {
method: "POST",
body: JSON.stringify({ username, password }),
headers: new Headers({
"Content-Type": "application/json",
}),
});
const json = await fetchResults.json();
if (!json.user || !json.sessionToken) {
alert(json.message);
return;
}
updateToken(json.sessionToken);
history.push("/home");
} catch (err) {
console.error(err);
}
};
return (
<ThemeProvider theme={theme}>
<Container component='main' maxWidth='xs'>
<CssBaseline />
<Box
sx={{
marginTop: 8,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Avatar sx={{ m: 1, bgcolor: "secondary.main" }}>
<LockOutlinedIcon />
</Avatar>
<Typography component='h1' variant='h5'>
Sign in
</Typography>
<Box
component='form'
onSubmit={handleSubmit}
noValidate
sx={{ mt: 1 }}
>
<TextField
margin='normal'
required
fullWidth
id='username'
label='Username'
name='username'
autoComplete='username'
value={username}
onChange={handleUsername}
autoFocus
/>
<TextField
margin='normal'
required
fullWidth
name='password'
label='Password'
type='password'
id='password'
value={password}
onChange={handlePassword}
autoComplete='current-password'
/>
<FormControlLabel
control={<Checkbox value='remember' color='primary' />}
label='Remember me'
/>
<Button
type='submit'
fullWidth
variant='contained'
sx={{ mt: 3, mb: 2 }}
>
Sign In
</Button>
<Grid container>
<Grid item xs>
<Link href='#' variant='body2'>
Forgot password?
</Link>
</Grid>
<Grid item>
<Link onClick={toggleView} variant='body2'>
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</Box>
</Box>
<Copyright sx={{ mt: 8, mb: 4 }} />
</Container>
</ThemeProvider>
);
}
import React, { useState } from "react";
import Login2 from "./Login2";
import Signup from "./Signup";
export default function Auth({ sessionToken, updateToken }) {
const [loginShowing, setLoginShowing] = useState(true);
const toggleView = () => setLoginShowing(!loginShowing);
return (
<div>
{loginShowing ? (
<Login2
token={sessionToken}
updateToken={updateToken}
toggleView={toggleView}
/>
) : (
<Signup
updateToken={updateToken}
token={sessionToken}
toggleView={toggleView}
/>
)}
</div>
);
}

Using a checkbox with Material UI to change the boolean value of an attribute within redux

I have a form that is controlled by redux state and I need to use checkboxes that will change the value of certain attributes of state from true to false and back again. The way that I have it written, I can click the checkbox and it updates the attribute to true but when I try to click on it again, it doesn't uncheck and it doesn't change state.
Here is my form,
import React, { useEffect } from 'react';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import TextField from '#material-ui/core/TextField';
import {Link} from 'react-router-dom'
import Grid from '#material-ui/core/Grid';
import Box from '#material-ui/core/Box';
import EditIcon from '#material-ui/icons/Edit';
import Typography from '#material-ui/core/Typography';
import { makeStyles } from '#material-ui/core/styles';
import Container from '#material-ui/core/Container';
import {connect} from 'react-redux'
import {updateProfileForm, setProfileForm} from '../actions/updateProfileActions'
import { update } from '../actions/currentUserActions'
import FormControl from '#material-ui/core/FormControl';
import Checkbox from '#material-ui/core/Checkbox';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormGroup from '#material-ui/core/FormGroup';
import FormLabel from '#material-ui/core/FormLabel';
import { TimePicker } from "#material-ui/pickers"
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '}
<Link color="inherit" href="https://material-ui.com/">
NTXASN
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
}
const useStyles = makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(3),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
const UpdateProfileForm =({updateFormData, updateProfileForm, update, history, currentUser, setProfileForm })=> {
const useUpdateProfileForm = () => {
useEffect(()=> {
setProfileForm(currentUser.attributes)
}, [])
}
useUpdateProfileForm()
const classes = useStyles();
const handleChange = event => {
const {name, value } = event.target
const updatedFormInfo = {
...updateFormData,
[name]: value
}
updateProfileForm(updatedFormInfo)
}
const handleBoolean = event => {
const {name, value } = event.target
console.log(event.target.checked)
const updatedFormInfo = {
...updateFormData,
[name]: !value
}
updateProfileForm(updatedFormInfo)
console.log(event.target.checked)
}
const handleSubmit = event =>{
event.preventDefault()
update(updateFormData, history)
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<EditIcon />
</Avatar>
<Typography component="h1" variant="h5">
Update your Driver Profile
</Typography>
<form className={classes.form} noValidate onSubmit={handleSubmit}>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
name="name"
variant="outlined"
required
fullWidth
id="name"
label="Name"
autoFocus
onChange={handleChange}
value={updateFormData.name}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
onChange={handleChange}
value={updateFormData.email}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="phone_number"
label="Phone Number"
name="phone_number"
autoComplete="phone number"
onChange={handleChange}
type="tel"
value={updateFormData.phone_number}
/>
</Grid>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">Select Available Days</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={updateFormData.monday} onChange={handleBoolean} name="monday" />}
label="Monday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.tuesday} onChange={handleBoolean} name="tuesday" />}
label="Tuesday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.wednesday} onChange={handleBoolean} name="wednesday" />}
label="Wednesday"
/>
</FormGroup>
</FormControl>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">-</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={updateFormData.thursday} onChange={handleBoolean} name="thursday" />}
label="Thursday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.friday} onChange={handleBoolean} name="friday" />}
label="Friday"
/>
<FormControlLabel
control={<Checkbox checked={updateFormData.saturday} onChange={handleBoolean} name="saturday" />}
label="Saturday"
/>
</FormGroup>
</FormControl>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">-</FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={updateFormData.sunday} onChange={handleBoolean} name="sunday" />}
label="Sunday"
/>
</FormGroup>
</FormControl>
</Grid>
<br/>
<Grid>
<TimePicker autoOk label="Hour Availability Lower Limit" value={updateFormData.availability_hours_lower} name="availability_hours_lower" onChange={handleChange}/>
<TimePicker autoOk label="Hour Availability Upper Limit" value={updateFormData.availability_hours_lower} name="availability_hours_upper" onChange={handleChange}/>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Update Profile
</Button>
</form>
</div>
<Box mt={5}>
<Copyright />
</Box>
</Container>
);
}
const mapStateToProps = state => {
return{
currentUser: state.currentUser,
updateFormData: state.updateProfile
}
}
export default connect(mapStateToProps, {updateProfileForm, update, setProfileForm})(UpdateProfileForm)
here are my actions
export const updateProfileForm = (formData) => {
return {
type: "UPDATE_PROFILE_FORM",
formData: formData
}
}
export const resetProfileForm = () => {
return {
type: "RESET_PROFILE_FORM",
}
}
export const setProfileForm = (formData) => {
return {
type: 'SET_PROFILE_FORM',
formData: formData
}
}
here is my reducer
const initialState = {
name: '',
email: '',
phone_number: '',
monday: false,
tuesday: false,
wednesday: false,
thursday: false,
friday: false,
saturday: false,
sunday: false,
availability_hours_lower: '',
availability_hours_upper: ''
}
const updateProfileReducer = (state = initialState, action) => {
switch(action.type){
case "UPDATE_PROFILE_FORM":
console.log(action.formData)
return action.formData
case "RESET_SIGNUP_FORM":
return initialState
case "SET_PROFILE_FORM":
return action.formData
default:
return state
}
}
export default updateProfileReducer
As I stated, right now when I click the check box it updates redux state from false to true and renders a checked box but when I try to uncheck the box, redux state is not updated and it does not uncheck the box. Any help would be greatly appreciated!
You should use the checked attribute to check if the checkbox is on or not.
const handleBoolean = (event) => {
const { name, checked } = event.target
updateProfileForm({
...updateFormData,
[name]: checked,
})
}
This is unrelated to the question but you can refactor your handleChange and handleBoolean functions by updating the reducer to apply the changes to the existing state.
const updateProfileReducer = (state = initialState, action) => {
switch (action.type) {
case 'UPDATE_PROFILE_FORM':
return { ...state, ...action.formData }
// ...
default:
return state
}
}
const handleChange = (event) => {
const { name, value } = event.target
updateProfileForm({
[name]: value,
})
}
const handleBoolean = (event) => {
const { name, checked } = event.target
updateProfileForm({
[name]: checked,
})
}
Btw, you don't even need to manage the form state in Redux since it's a local state. I would recommend using React Hook Form to simplify the code.

React Final Form Material UI creating double lines on input field

I have a strange thing happening where I can't really figure out what is creating those double input field lines in my code (see the picture). I have literally copied the code from the official website (React Final Form) and it doesn't seem to be happening there. Does it have something to do with the browser compatibility?
My code:
import React from 'react';
import { Form } from 'react-final-form';
import {
TextField,
Select,
} from 'mui-rff';
import {
Grid,
Button,
MenuItem,
} from '#material-ui/core';
import { createMuiTheme } from '#material-ui/core/styles';
import { ThemeProvider } from '#material-ui/styles';
const theme = createMuiTheme({
palette: {
primary: {
main: '#008080',
},
}
})
const onSubmit = async values => {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const validate = values => {
const errors = {};
if (!values.firstName) {
errors.firstName = 'Required';
}
if (!values.lastName) {
errors.lastName = 'Required';
}
if (!values.email) {
errors.email = 'Required';
}
return errors;
};
const formFields = [
{
size: 6,
field: (
<TextField
label="First Name"
name="firstName"
margin="none"
required={true}
/>
),
},
{
size: 6,
field: (
<TextField
label="Last Name"
name="lastName"
margin="none"
required={true}
/>
),
},
{
size: 12,
field: (
<TextField
type="email"
label="Email"
name="email"
margin="none"
required={true}
/>
),
},
{
size: 12,
field: <TextField name="notes" multiline label="Notes" margin="none" />,
},
{
size: 12,
field: (
<Select
name="city"
label="Select a City"
formControlProps={{ margin: 'none' }}
>
<MenuItem value="London">London</MenuItem>
<MenuItem value="New York">New York</MenuItem>
<MenuItem value="Tallinn">Tallinn</MenuItem>
</Select>
)
}
];
const FinalForm = () => {
return (
<div className="col s12 m6">
<div className="card-panel grey lighten-3">
<h5>Please fill out this form</h5>
<Form
onSubmit={onSubmit}
validate={validate}
render={({ handleSubmit, form, submitting, pristine }) => (
<form onSubmit={handleSubmit} noValidate>
<Grid container alignItems="flex-start" spacing={2}>
{formFields.map((item, idx) => (
<Grid item xs={item.size} key={idx}>
{item.field}
</Grid>
))}
<Grid item style={{ marginTop: 16 }}>
<Button
type="button"
variant="contained"
onClick={form.reset}
disabled={submitting || pristine}
>
Reset
</Button>
</Grid>
<Grid item style={{ marginTop: 16 }}>
<ThemeProvider theme={theme}>
<Button
variant="contained"
type="submit"
color='primary'
disabled={submitting}
>
Submit
</Button>
</ThemeProvider>
</Grid>
</Grid>
</form>
)}
/>
</div>
</div>
)
};
export default FinalForm;

How to InputRef works in material ui core 3.9.2

In Material ui core 3.9.2
On inputRef={input => {
this.input = input;
}}
Error Shows
TypeError: Cannot set property 'input' of undefined
If we use this.email instead of this.input
Then Error Shows
TypeError: Cannot set property 'email' of undefined
This is TextField Code
<TextField
id="login-email"
label="Email/MobileNo"
required
fullWidth
type="email"
className={classes.textField}
inputRef={el => {
this.input = el;
}}
or
inputRef={el => this.email = el;}
margin="normal"
/>
Here is solution for functional component.
import React, { useRef, Component } from 'react'
import { TextField, Button } from '#material-ui/core'
import SendIcon from '#material-ui/icons/Send'
export default function MultilineTextFields() {
const valueRef = useRef('') //creating a refernce for TextField Component
const sendValue = () => {
return console.log(valueRef.current.value) //on clicking button accesing current value of TextField and outputing it to console
}
return (
<form noValidate autoComplete='off'>
<div>
<TextField
id='outlined-textarea'
label='Content'
placeholder='Write your thoughts'
multiline
variant='outlined'
rows={20}
inputRef={valueRef} //connecting inputRef property of TextField to the valueRef
/>
<Button
variant='contained'
color='primary'
size='small'
endIcon={<SendIcon />}
onClick={sendValue}
>
Send
</Button>
</div>
</form>
)
}
It was this issue
I solve it by changing
From:
import { makeStyles } from '#material-ui/styles';
import { Link } from "react-router-dom";
import TextField from '#material-ui/core/TextField';
import { Button, Grid } from '#material-ui/core';
import { red } from '#material-ui/core/colors';
const useStyles = makeStyles({
container: {
background: red,
},
});
export default function Hook() {
const classes = useStyles();
const [values, setValues] = React.useState({
email: '',
password: '',
});
const handleChange = name => event => {
setValues({ ...values, [name]: event.target.value });
};
const handleSubmit = event => {
event.preventDefault();
// alert(this.password);
console.log("event ", event);
// console.log("E ",this.email)
// console.log("p ",this.password)
// console.log(this.email.value)
// console.log(this.password.value)
};
return <form className={classes.container} validate="true" onSubmit={handleSubmit} autoComplete="off">
<TextField
id="login-email"
label="Email/MobileNo"
required
fullWidth
type="email"
inputRef={el => this.email = el}
onChange={handleChange('email')}
margin="normal"
/>
<TextField
id="login-password"
label="Password"
required
fullWidth
type="password"
inputRef={el => this.password = el}
onChange={handleChange('password')}
margin="normal"
/>
<Grid container className="m-y-20" justify="center">
<Grid item md={5}>
<Button className="login-submit-btn" type="submit">Login</Button>
<Link className="t-d-none" to="/">
<Button className="login-new-btn">Create New Account</Button>
</Link>
</Grid>
<Grid item md={7}>
<span className="p-x-15">
<Link to="/forgopassword" className="black-clr">
Forgot Your Password?
</Link>
</span>
</Grid>
</Grid>
</form>
}
To:
import React from 'react';
import { Link } from "react-router-dom";
import TextField from '#material-ui/core/TextField';
import { Button, Grid } from '#material-ui/core';
class LoginForm extends React.Component {
render() {
const handleSubmit = event => {
event.preventDefault();
// alert(this.password);
console.log("event ", event);
// console.log("E ",this.email)
// console.log("p ",this.password)
console.log(this.email.value)
console.log(this.password.value)
};
return <form validate="true" onSubmit={handleSubmit} autoComplete="off">
<TextField
id="login-email"
label="Email/MobileNo"
required
fullWidth
type="email"
inputRef={el => this.email = el}
margin="normal"
/>
<TextField
id="login-password"
label="Password"
required
fullWidth
type="password"
inputRef={el => this.password = el}
margin="normal"
/>
<Grid container className="m-y-20" justify="center">
<Grid item md={5}>
<Button className="login-submit-btn" type="submit">Login</Button>
<Link className="t-d-none" to="/">
<Button className="login-new-btn">Create New Account</Button>
</Link>
</Grid>
<Grid item md={7}>
<span className="p-x-15">
<Link to="/forgopassword" className="black-clr">
Forgot Your Password?
</Link>
</span>
</Grid>
</Grid>
</form>
}
}
export default LoginForm;
try
<TextField
id="login-email"
label="Email/MobileNo"
required
fullWidth
type="email"
className={classes.textField}
inputRef={input => this.input = input}
margin="normal"
/>
with this you are returning nothing
inputRef={input => {
this.input = input;
}}

Resources