React Hooks custom validation in form cannot update multiple states - reactjs

If all fields were left empty during the firing of handleValidate(), only the states of confirmPasswordIsError and confirmPassword in error would be updated but the others. I have no idea what went wrong?
function Register() {
const classes = useStyles();
const { handleRegister } = useContext(AuthenticationContext);
const [form, setForm] = useState({
username: '',
password: '',
confirmPassword: ''
})
const [error, setError] = useState({
usernameIsError: false,
usernameError: '',
passwordIsError: false,
passwordError: '',
confirmPasswordIsError: false,
confirmPasswordError: ''
});
const handleValidate = () => {
if (!form.username) {
setError({
...error,
usernameIsError: true,
usernameError: '用戶名稱不能留空'
})
}
if (!form.password) {
setError({
...error,
passwordIsError: true,
passwordError: '密碼不能留空'
})
}
if (!form.confirmPassword) {
setError({
...error,
confirmPasswordIsError: true,
confirmPasswordError: '確認密碼不能留空'
})
return false;
}
// if (form.confirmPassword !== form.password) {
// setError({
// ...error,
// confirmPasswordIsError: true,
// confirmPasswordError: '確認密碼與密碼不相同'
// })
// return false;
// }
return true;
}
const handleChange = (e) => {
const { name, value } = e.target
setForm({
...form,
[name]: value
})
}
const handleSubmit = (e) => {
e.preventDefault();
const isFormValid = handleValidate();
console.log(isFormValid)
if (isFormValid) {
handleRegister(form.username, form.password)
}
}
return (
<Container>
<Grid
container
justify="center"
>
<Grid item>
<Paper className={classes.paper} elevation={3} >
<form noValidate autoComplete="off" onSubmit={handleSubmit}>
<TextField
error={error.usernameIsError}
helperText={error.usernameError}
className={classes.input}
fullWidth
required
name="username"
size="small"
label="帳號"
variant="outlined"
value={form.username}
onChange={handleChange}
/>
<TextField
error={error.passwordIsError}
helperText={error.passwordError}
className={classes.input}
fullWidth
required
name="password"
size="small"
label="密碼"
type="password"
variant="outlined"
value={form.password}
onChange={handleChange}
/>
<TextField
error={error.confirmPasswordIsError}
helperText={error.confirmPasswordError}
className={classes.input}
fullWidth
required
name="confirmPassword"
size="small"
label="確認密碼"
type="password"
variant="outlined"
value={form.confirmPassword}
onChange={handleChange}
/>
<Button className={classes.button} fullWidth variant="contained" color="primary" type="submit">註冊</Button>
</form>
</Paper>
</Grid>
</Grid>
</Container>
)
}

If enqueueing multiple updates within a render cycle use a functional state update so the subsequent updates don't overwrite the previously enqueued updates.
When you use normal updates you are spreading in the error state closed over in callback scope from the current render cycle, so each update blows away the previous update. Functional state updates allow you to update from the previous state.
const handleValidate = () => {
if (!form.username) {
setError(error => ({
...error,
usernameIsError: true,
usernameError: '用戶名稱不能留空'
}))
}
if (!form.password) {
setError(error => ({
...error,
passwordIsError: true,
passwordError: '密碼不能留空'
}))
}
if (!form.confirmPassword) {
setError(error => ({
...error,
confirmPasswordIsError: true,
confirmPasswordError: '確認密碼不能留空'
}))
return false;
}
if (form.confirmPassword !== form.password) {
setError(error => ({
...error,
confirmPasswordIsError: true,
confirmPasswordError: '確認密碼與密碼不相同'
}))
return false;
}
return true;
}

Related

This onBlur is not working the way I want

Here I want this onBlur to show another text or input field which is working but whenever i clear the value it should be hiding but right now it is not working the way I want. In easy words whenever I enter content in input field and loose the focus it show sub input field but whenever clear it all it should be hiding but it is not working here is the code
<Typography
color="#05445E"
fontFamily="'Jost', sans-serif"
fontSize={15}
>
Email
</Typography>
<Input
fullWidth
name="email"
value={user.email}
onChange={handleChange}
disableUnderline={true}
onBlur={handleOTP}
className={classes.inputEmail}
endAdornment={
<>
{user.clearEmail ? (
<IconButton
onClick={() => clearValue("email", "clearEmail")}
>
<ClearIcon />
</IconButton>
) : (
""
)}
</>
}
/>
{showSecondInput && (
<>
<Typography
color="#05445E"
fontFamily="'Jost', sans-serif"
fontSize={15}
sx={{ mt: "15px" }}
>
Enter OTP
</Typography>
<Input
className={classes.inputEmail}
fullWidth
type="password"
/>
</>
)}
This is the states I have used
const [user, update_user] = useState({
user_name: "",
email: "",
clearUser: false,
clearEmail: false,
});
const clearValue = (key, show) => {
update_user({ ...user, [key]: "", [show]: false });
};
const [showSecondInput, setShowSecondInput] = useState(false);
const handleOTP = (e) => {
const { name:key } = e.target;
if(key === "email") setShowSecondInput({ ...showSecondInput, [key]: "", clearEmail: false });
};
const handleChange = (event) => {
const { name: key, value } = event.target;
if (value) {
if (key === "user_name")
update_user({ ...user, [key]: value, clearUser: true });
else if (key === "email")
update_user({
...user,
[key]: value,
clearEmail: true,
});
} else
update_user({
...user,
[key]: "",
clearUser: false,
clearEmail: false,
});
};
The clearValue function is working smoothly without any problem the problem is on blur event..
You could do this by checking for the name in case you have more onBlur with the handleOTP function. Then checking if the value is a empty string and based on that set the state.
const handleOTP = (e) => {
const { name, value } = e.target;
if (name !== "email") return;
if (value === "") {
setShowSecondInput(true);
} else {
setShowSecondInput(false);
}
};

can't change the data related to the ID in Reactjs when update form

I creating a BlogSite using Reactjs
I have trouble when I update a post. After I clicked the update icon, the form data fill according to the post id. but I can't input anything it's like as readonly
Get data according to id
<Button style={{ color: 'white' }} size='small' onClick={() =>updatePost(post._id)}>
<MoreHorizIcon />
</Button>
dispatch action
const updatePost = (id) => {dispatch(getPost(id))}
action code
try {
dispatch({ type: POST_GET_REQUEST })
const { data } = await axios.get(`http://localhost:5000/posts/get/${id}`)
dispatch({
type: POST_GET_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: POST_GET_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
reducer code
switch (action.type) {
case POST_GET_REQUEST:
return { loading: true, post: [] }
case POST_GET_SUCCESS:
return { loading: false, success: true, post: action.payload }
case POST_GET_FAIL:
return { loading: false, error: action.payload }
default:
return state
}
}
store code
const reducer = combineReducers({postGet: postGetReducer})
FormScreen.js
import { useDispatch, useSelector } from 'react-redux'
import { TextField, Typography, Paper } from '#mui/material'
import { createPost } from '../actions/postsAction'
const FormScreen = () => {
const dispatch = useDispatch()
const postGet = useSelector((state) => state.postGet)
const [newUser, setNewAuthor] = useState({
creator: '',
title: '',
message: '',
tags: '',
photo: '',
})
const handleChange = (e) => {
setNewAuthor({ ...newUser, [e.target.name]: e.target.value })
}
const handlePhoto = (e) => {
setNewAuthor({ ...newUser, photo: e.target.files[0] })
}
const handleSubmit = (e) => {
e.preventDefault()
const formData = new FormData()
formData.append('photo', newUser.photo)
formData.append('creator', newUser.creator)
formData.append('title', newUser.title)
formData.append('message', newUser.message)
formData.append('tags', newUser.tags)
dispatch(createPost(formData))
}
return (
<Paper>
<form
onSubmit={handleSubmit}
encType='multipart/form-data'
style={{
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'center',
margin: '20px',
padding: '10px',
}}
>
<Typography variant='h5'>Creating a Memory</Typography>
<TextField
type='text'
label='Creator'
fullWidth
variant='outlined'
placeholder='Creator'
name='creator'
value={postGet.post.creator} // value={newUser.creator}
onChange={handleChange}
/>
<TextField
type='text'
fullWidth
variant='outlined'
label='Title'
placeholder='Title'
name='title'
value={postGet.post.title} // value={newUser.title}
onChange={handleChange}
/>
<TextField
type='text'
placeholder='Message'
label='Message'
fullWidth
variant='outlined'
name='message'
value={postGet.post.message} // value={newUser.message}
onChange={handleChange}
/>
<TextField
type='text'
placeholder='Tags'
label='Tags'
fullWidth
variant='outlined'
name='tags'
value={postGet.post.tags} // value={newUser.tags}
onChange={handleChange}
/>
<TextField
type='file'
accept='.png, .jpg, .jpeg'
fullWidth
name='photo'
onChange={handlePhoto}
/>
<div className='bg'></div>
<TextField type='submit' />
<TextField type='button' value='Clear' />
</form>
</Paper>
)
}
export default FormScreen
Create post success. I create post and update post in same form

How to pass the value of checkbox in react?

God Day, I'm trying to pass a Boolean value of the checkbox using onChange but onChange is already use to toggle the checkbox value. I dont have idea on what will be the other way around. Please guide me Thank you much.
function ExperienceForm() {
const [postData, setPostData] = useState({intern: ''});
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
dispatch(createPost(postData))
}
const [state, setState] = React.useState({
intern: false,
});
const handleChange = (event) => {
setState({ ...state, [event.target.name]: event.target.checked });
console.log(state.intern);
};
return (
<form autoComplete="off" noValidate className="form" onSubmit={handleSubmit}>
<FormControlLabel control={
<Checkbox
checked={state.intern}
onChange={handleChange ((e) => setPostData({ ...postData, intern: e.target.value }))}
name="intern"
color="primary"
value={state.intern}
/>
}
label="Intern"
/><br/>
<Button className="button" variant="container" color="primary" size="large" type="submit" block>Submit</Button>
</form>
);
}
export default ExperienceForm;
I don't see code of your <FormControlLabel /> and <Checkbox /> components, but with regular html input you can do it like this:
import React, { useState } from "react";
function ExperienceForm() {
const [postData, setPostData] = useState({ intern: false });
const [state, setState] = useState({ intern: false });
const handleChange = ({ target }) => {
setState({ ...state, [target.name]: target.checked });
setPostData({ ...postData, intern: target.checked });
};
return (
<form autoComplete="off" noValidate className="form">
<h2>postData.intern: {postData.intern.toString()}</h2>
<h2>state.intern: {state.intern.toString()}</h2>
<input
type="checkbox"
checked={state.intern}
onChange={handleChange}
name="intern"
color="primary"
value={state.intern}
/>
<button type="submit">Submit</button>
</form>
);
}
export default ExperienceForm;

React TypeScript: Multiple State updates but only first one gets applied

I made a sandBox here https://codesandbox.io/s/old-mountain-xl7nz when you don't full in any of the form inputs I expect to see 3 errors but I only get one. I don't understand why
import React, { ChangeEvent, useState } from 'react';
import { Link } from 'react-router-dom';
import loadingImg from '../images/loading.svg';
const Join: React.FC = () => {
const [state, setState] = useState({
email: '',
emailError: '',
fullName: '',
fullNameError: '',
loading: false,
password: '',
passwordError: '',
});
const {
email,
emailError,
fullName,
fullNameError,
password,
passwordError,
} = state;
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
event.persist();
setState((prev) => ({
...prev,
[event.target.id]: event.target.value,
}));
};
const onSubmit = (event: React.FormEvent) => {
event.preventDefault();
if (validate('fullName') && validate('email') && validate('password')) {
console.log('FIRE FORM');
}
};
const onBlur = (event: ChangeEvent<HTMLInputElement>) => {
validate(event.target.id);
};
const validate = (id: string) => {
switch (id) {
case 'fullName':
if (!/^.{6,7}$/.test(fullName)) {
setState((prev) => ({ ...prev, fullNameError: 'err' }));
return false;
} else {
setState((prev) => ({ ...prev, fullNameError: '' }));
return true;
}
break;
case 'email':
if (!/\S+#\S+\.\S+/.test(email)) {
setState((prev) => ({ ...prev, emailError: 'err' }));
return false;
} else {
setState((prev) => ({ ...prev, emailError: '' }));
return true;
}
break;
default:
if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/.test(password)) {
setState((prev) => ({ ...prev, passwordError: 'err' }));
return false;
} else {
setState((prev) => ({ ...prev, passwordError: '' }));
return true;
}
}
};
return (
<div className='join'>
<h2>JOIN</h2>
<h3>some subheading</h3>
<form onSubmit={onSubmit}>
<label>Name</label>
<input
type='text'
placeholder='Full name'
id='fullName'
value={fullName}
onChange={onChange}
onBlur={onBlur}
/>
{fullNameError}
<label>Email</label>
<input
type='text'
placeholder='Email address'
id='email'
value={email}
onChange={onChange}
onBlur={onBlur}
/>
{emailError}
<label>Password</label>
<input
type='password'
placeholder='Create a password'
id='password'
value={password}
onChange={onChange}
onBlur={onBlur}
/>
{passwordError}
<button color='primary'>
{!state.loading ? (
'Join Now'
) : (
<img src={loadingImg} alt='loadingd' className='loading' />
)}
</button>
<div className='join--terms'>
By joining, you agree to our
<Link to={{ pathname: '/terms' }}> Terms of Service</Link> and
<Link to={{ pathname: '/terms' }}> Privacy Policy</Link>
</div>
</form>
</div>
);
};
export { Join };
your conditions are not evaluating when first condition is false...
so you better to do something like this....
const onSubmit = (event: React.FormEvent) => {
event.preventDefault();
const fullNameValidation = validate('fullName')
const emailValidation = validate('email')
const passwordValidation = validate('password')
if (fullNameValidation && emailValidation && passwordValidation) {
console.log('FIRE FORM');
}
};

Redux Form State

How would I push these values to the employment history state? I am getting the read out in form but not to the initial state?
Should I use formSelector to push values to state? This has been a nightmare for me, as it is 99% finished except for getting everything to push to state. So that I can push everything where it needs to go once form is complete.
class EmploymentHistoryQuestion extends Component {
static propTypes = {
roles: PropTypes.array,
handleSubmit: PropTypes.func,
driverProfile: PropTypes.object,
fields: PropTypes.object,
whenUserCompletedQuestion: PropTypes.func.isRequired,
};
state = {
employmentHistory: {
employerName: '',
role: '',
startDate: '',
endDate: '',
description: '',
currentPosition: false,
},
};
handleChange = (field) => (response, value) => {
const { employmentHistory } = this.state;
const { whenUserCompletedQuestion } = this.props;
console.log(employmentHistory);
this.setState({
employmentHistory,
}, () => {
if (field === 'startDate' || field === 'endDate') {
whenUserCompletedQuestion({
[field]: response.value,
});
} else if (field === 'role') {
whenUserCompletedQuestion({
[field]: value,
});
} else if (field === 'employerName' || field === 'description') {
whenUserCompletedQuestion({
[field]: response.target.value,
});
}
});
};
render() {
const { fields, roles, handleSubmit, driverProfile, ...more } = this.props;
return (
<div className={bem.el('form')}>
<form onSubmit={handleSubmit}>
<Field
name={'employerName'}
label={translations.translate('driverProfile', 'employerName')}
component={renderTextField}
onChange={this.handleChange('employerName')}
fullWidth
required
/>
<div style={{ minHeight: '120px' }}>
<InputField
question={'startDate'}
onChange={this.handleChange('startDate')}
required
/>
</div>
<div style={{ minHeight: '120px' }}>
<InputField
question={'endDate'}
onChange={this.handleChange('endDate')}
required
/>
</div>
<Field
name={'role'}
component={renderSelectField}
value={this.state.role}
onChange={this.handleChange('role')}
required
fullWidth
>
{roles.map((role, index) =>
(<MenuItem
value={role}
key={index}
primaryText={translations.translate('vehicleTypes', role)}
/>)
)}
</Field>
<Field
name={'description'}
label={translations.translate('editJob', 'description')}
component={renderTextArea}
onChange={this.handleChange('description')}
fullWidth
/>
</form>
</div>
);
}
}
export default reduxForm({
form: 'employmentHistory',
})(EmploymentHistoryQuestion);

Resources