React Hooks custom validation in form cannot update multiple states

Viewed 85

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>
    )
}
1 Answers

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