0
votes

During user-registration, my backend informs the client if an email and/or username are currently in use by someone else. Below is the response logged into a webconsole thanks to Axios catch error.

I would like to map each email and username to the appropriate field. My form is based off of Material-UI and react-hook-form

Here is the example of the error response provided by my Axios Instance.

{
"email":["This email is already in use"],
"username":["This username is already in use"]
}

Here is my complete react form, I cut some things out to make it easier to read:

export default function SignUp()
{
    const { register, control, errors: fieldsErrors, handleSubmit } = useForm()

    const onSubmit = (data, e) => {
        console.log(data);

        axiosInstance
            .post(`api/user/register/`, {
                email: data.email,
                username: data.username,
                password: data.password,
            })
            .then((res) => {
                history.push('/login');
                console.log(res);
                console.log(res.data);
            })
            .catch(err => {
                console.error(err.response.data);
            }
            )
    };


    return (
        <Container component="main" maxWidth="xs">
            <CssBaseline />
            <div className={classes.paper}>
                <Avatar className={classes.avatar}></Avatar>
                <Typography component="h1" variant="h5">
                    Sign up
                </Typography>
                <form className={classes.form} noValidate onSubmit={handleSubmit(onSubmit)}>
                    <FormControl fullWidth variant="outlined">
                    <Grid container spacing={2}>
                        <Grid item xs={12}>
                                <Controller 
                                    name="email"
                                    as={
                                        <TextField
                                            variant="outlined"
                                            required
                                            fullWidth
                                            id="email"
                                            label="Email Address"
                                            name="email"
                                            autoComplete="email"
                                            error={Boolean(fieldsErrors.email)}
                                            onChange={
                                                (evt) =>
                                                {
                                                    let key = evt.currentTarget.name;
                                                    let value = evt.currentTarget.value;
                                                    handleChange({ [key]: value });
                                                }
                                            }
                                        />
                                    }
                                    control={control}
                                    defaultValue=""
                                    rules={{
                                        required: 'Required',
                                        pattern: {
                                        value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
                                        message: 'invalid email address'
                                        }
                                    }}
                                    />
                            </Grid>
                            <Grid item xs={12}>
                                <Controller
                                    name="username"
                                    as={
                                        <TextField
                                            variant="outlined"
                                            required
                                            fullWidth
                                            id="username"
                                            label="Username"
                                            name="username"
                                            onChange={
                                                (evt) => {
                                                    let key =  evt.currentTarget.name;
                                                    let value = evt.currentTarget.value;
                                                    handleChange({[key]: value});
                                                }
                                                }
                                        />
                                    }
                                    control={control}
                                    defaultValue=""
                                    rules={{
                                        required: 'Required',
                                        pattern: {
                                        value: /^(?!.*\.\.)(?!.*\.$)[^\W][\w.]{0,29}$/i,
                                        message: 'Invalid use of characters'
                                        }
                                    }}
                                />
                                {fieldsErrors.username?.type && <p>{fieldsErrors.username?.message}</p>}
                        </Grid>
                            <Grid item xs={12}>
                                <Controller
                                    name="password"
                                    as={
                                    
                                        <TextField
                                            variant="outlined"
                                            required
                                            fullWidth
                                            name="password"
                                            label="Password"
                                            type="password"
                                            id="password"
                                            autoComplete="current-password"
                                            onChange={
                                                (evt) =>
                                                {
                                                    let key = evt.currentTarget.name;
                                                    let value = evt.currentTarget.value;
                                                    handleChange({ [key]: value });
                                                }
                                            }
                                        />
                                    }
                                    control={control}
                                    defaultValue="" 
                                />  
                        </Grid> 
                    </Grid>
                    <Button
                        type="submit"
                        fullWidth
                        variant="contained"
                        color="primary"
                        className={classes.submit}
                        onClick={handleSubmit}
                    >
                        Sign Up
                    </Button>
                </FormControl>
                    
                </form>
            </div>
        </Container>
    );
}

I've managed to create the error into a webalert, but that did not look nice. Is there any simple way to implement my catch error into my form?

I use this field error :

{fieldsErrors.username?.type && <p>{fieldsErrors.username?.message}</p>}

As my Regex error to warn users of illegal characters in their username. I was thinking maybe I could add the errors there? But I do not know how to do that.

1

1 Answers

2
votes

You can leverage the useState hook to store errors from the API. Then you can use your template to render those errors in the same format that you display your regex errors.

On a side note, for security's sake it's not a great idea to tell a user that an email is already taken. But if your application doesn't have sensitive data then it's not a huge deal.

EDIT: example

export default function SignUp()
{
    const [ apiError, setApiError ] = useState(null)
    
    const onSubmit = (data, e) => {
        setApiError(null)

        axiosInstance
            //...
            .catch(err => {
                setApiError(err.response.data.message)
                console.error(err.response.data);
            }
            )
    };


    return (
    //....
    {apiError && <p>{apiError}</p>}
    //....
    )