I am using react, typescript, the react-hook-form library and 'yup' for form validation.
If the form is validated correctly using the react-hook-form library then I want an email to be sent using 'Email JS' library.
When the form is submitted, the handleSubmit function from react-hook-form library is called.
docs for handleSubmit
If there are no errors, then the sendEmail function should be called, and the email sent with Email JS.
I have put the code below, the important part is the form's 'onSubmit' property.
import React, { useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import { string, number, object, InferType } from "yup";
import { url } from "inspector";
import emailjs from "#emailjs/browser";
function onSubmit(values: Props) {}
type Props = InferType<typeof schema>;
const schema = object({
firstName: string().required("First name is required"),
lastName: string().required("Last name is required"),
});
function FormEmail() {
const form = useRef(null);
const [value, setValue] = useState<string | undefined>();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Props>({
resolver: yupResolver(schema),
});
const sendEmail = (e: { preventDefault: () => void }) => {
e.preventDefault();
emailjs
.sendForm(
'YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', form.current!, 'YOUR_PUBLIC_KEY'
)
.then(
(result: { text: any }) => {
console.log(result.text);
},
(error: { text: any }) => {
console.log(error.text);
}
);
};
return (
<div>
<form onSubmit={handleSubmit(onSubmit)} ref={form}>
<h3>First Name</h3>
<input
id="firstName"
type="text"
{...register("firstName")}
/>
<span className="error">{errors?.firstName?.message}</span>
<h3>Last Name</h3>
<input
id="lastName"
type="text"
{...register("lastName")}
/>
<span className="error">{errors?.lastName?.message}</span>
<button type="submit">
Submit
</button>
</form>
</div>
);
}
export default FormEmail;
Any help on this would be appreciated!
Related
I'm trying to validate a simple form with a single input just for practice. I also don't want the value that the user types in the input to disappear after page refresh, for that reason, I did a little bit of searching and found out about saving that data using localStorage. After trying to implement that for a while, I managed to do that, when I refresh the page, the value is still there. However, now, when I'm trying to validate the form using useForm from react-hook-form, It just doesn't work for some reason, when I try to use that same useForm logic with an input without using localStorage, It works just fine, but while trying to add localStorage functionality, then it doesn't. I hope I'm describing my problem at least okey, here's the code :
import React, {useEffect, useState } from "react";
import "./App.css"
import { useForm } from "react-hook-form";
const getForm = () => {
const storedValues = localStorage.getItem("form");
if(!storedValues) return {
name: "",
age: ""
}
return JSON.parse(storedValues);
}
function Home() {
const [values, setValues] = useState(getForm)
const {register, handleSubmit, watch} = useForm();
const handleChange = (e) => {
setValues((previousValues) => ({
...previousValues,
[e.target.name]: e.target.value,
}))
}
const onSubmit = async data => { console.log(data); };
useEffect(()=>{
localStorage.setItem("form", JSON.stringify(values))
}, [values])
return (
<div className="container">
<form onSubmit={handleSubmit(onSubmit)}>
<input value={values.name} onChange={handleChange} name="name" placeholder="name" />
<input value={values.age} onChange={handleChange} name="age" placeholder="age"/>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default Home;
This code works fine since I'm not adding useForm register to the input, but if I do that, then It gets buggy, like this :
<input value={values.name} onChange={handleChange} name="name" placeholder="name" {...register("name")} />
The latest code only works If I remove the value atrribute from the input, but I can't do that, If I do, I can't use localStorage anymore.
Looking at the documentation, you had the syntax a little off with your register function. That function takes a second argument, which is an object of props, and that is where you want to define value, name and onChange.
Like this:
<input
placeholder="name"
{...register("name", {
onChange: handleChange,
name: "name",
value: values.name
})}
/>
Here is the full code I have working on a codesandbox. That's really all I changed, expect removing the watch import.
import React, { useEffect, useState } from "react";
import "./styles.css";
import { useForm } from "react-hook-form";
const getForm = () => {
const storedValues = localStorage.getItem("form");
if (!storedValues)
return {
name: "",
age: ""
};
return JSON.parse(storedValues);
};
function Home() {
const [values, setValues] = useState(getForm);
const { register, handleSubmit } = useForm();
const handleChange = (e) => {
setValues((previousValues) => ({
...previousValues,
[e.target.name]: e.target.value
}));
};
const onSubmit = async (data) => {
console.log(data);
};
useEffect(() => {
localStorage.setItem("form", JSON.stringify(values));
}, [values]);
return (
<div className="container">
<form onSubmit={handleSubmit(onSubmit)}>
<input
placeholder="name"
{...register("name", {
onChange: handleChange,
name: "name",
value: values.name
})}
/>
<input
value={values.age}
onChange={handleChange}
name="age"
placeholder="age"
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default Home;
I have a form that have an onSubmit, in that callback I have a uploady.showFileUpload(), but the code after uploady.showFileUpload() is executed.
Now the question is how can I wait for uploady, and then execute the rest of the code?
const handleSubmit2 = useCallback((e)=> {
uploady.showFileUpload(); //(HERE SHOULD WAIT FOR IT TO FINISH FILE SELECT)
//OTHER CODE
});
This codesandbox should be helpful in the case of using Uploady with a form:
https://codesandbox.io/s/react-uploady-inside-form-ys1wx
The idea is that you show the file selection prompt separately from submitting the form:
import React, { useState, useCallback, useMemo, forwardRef } from "react";
import Uploady, {
useUploadyContext
} from "#rpldy/uploady";
import { asUploadButton } from "#rpldy/upload-button";
const MyUploadField = asUploadButton(
forwardRef(({ onChange, ...props }, ref) => {
return (
<div {...props} ref={ref} id="form-upload-button" title={text}>
Select file
</div>
);
})
);
const MyForm = () => {
const [fields, setFields] = useState({});
const [fileName, setFileName] = useState(null);
const uploadyContext = useUploadyContext();
const onSubmit = useCallback(() => {
uploadyContext.processPending({ params: fields });
}, [fields, uploadyContext]);
const onFieldChange = useCallback(
(e) => {
setFields({
...fields,
[e.currentTarget.id]: e.currentTarget.value
});
},
[fields, setFields]
);
return (
<form>
<MyUploadField autoUpload={false} />
<input
onChange={onFieldChange}
id="field-name"
type="text"
placeholder="your name"
/>
<SubmitButton
id="form-submit"
type="button"
onClick={onSubmit}
>
Submit Form
</SubmitButton>
</form>
);
};
<Uploady
clearPendingOnAdd
destination={{ url: "[upload-url]" }}
multiple={false}
>
<MyForm />
</Uploady>
Selection is achieved by using the asUploadButton HOC. You can of course do so yourself as you did with uploady.showFileUpload();.
Then, the submit button uses uploady's processPending method to start uploading.
I'm creating a register form in react with validation. The values i'm asking is Username, Email, password + (controll password). Everything about the form works, validations, Errors and if you click sign up you go to a new page. Now i want to extract the values to a my MySql database. I succeed in putting stuff in my database so the link works but i can't get the values of what i typed in the form.
I have tried
onChange={(e) => {
setUsernameReg(e.target.value);
}}
(see commented item)
But when i tried this I couldn't fill anything in Username. The code for the other inputs (email, password) is the same apart from the names.
So in short I want to get the value what you typed in a textbox to my database.
Code: FormSignup.js
import React, { useEffect, useState } from 'react';
import Axios from 'axios';
import validate from './validateInfo';
import useForm from './useForm';
import './Form.css';
const FormSignup = ({ submitForm }) => {
const { handleChange, handleSubmit, values, errors } = useForm(
submitForm,
validate
);
const [usernameReg, setUsernameReg] = useState("");
const [emailReg, setEmailReg] = useState("");
const [passwordReg, setPasswordReg] = useState("");
Axios.defaults.withCredentials = true;
const register = () => {
Axios.post("http://localhost:3001/register", {
username: usernameReg,
password: passwordReg,
email: emailReg,
}).then((response) => {
console.log(response);
});
};
return (
<div className='form-content-right'>
<form onSubmit={handleSubmit} className='form' noValidate>
<h1>
Get started with us today! Create your account by filling out the
information below.
</h1>
<div className='form-inputs'>
<label className='form-label'>Username</label>
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={values.username}
onChange={handleChange}
/*
//onChange={(e) => {
// setUsernameReg(e.target.value);
//}}
*/
/>
Code UseForm.js
import { useState, useEffect } from 'react';
import Axios from 'axios';
const useForm = (callback, validate) => {
const [values, setValues] = useState({
username: '',
email: '',
password: '',
password2: ''
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [usernameReg, setUsernameReg] = useState("");
const [emailReg, setEmailReg] = useState("");
const [passwordReg, setPasswordReg] = useState("");
Axios.defaults.withCredentials = true;
const register = () => {
Axios.post("http://localhost:3001/register", {
username: usernameReg,
password: passwordReg,
email: emailReg,
}).then((response) => {
console.log(response);
});
};
const handleChange = e => {
const { name, value } = e.target;
setValues({
...values,
[name]: value
});
};
const handleSubmit = e => {
e.preventDefault();
setErrors(validate(values));
setIsSubmitting(true);
};
useEffect(
() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
}
},
[errors]
);
return { handleChange, handleSubmit, values, errors };
};
export default useForm;
The code is from https://www.youtube.com/watch?v=KGFG-yQD7Dw&t and https://www.youtube.com/watch?v=W-sZo6Gtx_E&t
By using the value prop of the input, you turn it into a controled input element and thus need to update its value via a state variable. So this:
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={values.username}
onChange={handleChange}
/*
//onChange={(e) => {
// setUsernameReg(e.target.value);
//}}
*/
/>
Should just be this:
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={usernameReg}
onChange={e => setUsernameReg(e.target.value)}
/>
Note that this only answers this part:
I succeed in putting stuff in my database so the link works but i can't get the values of what i typed in the form
So this is how you can access those values. I can't guide you on how to get those values all the way to your DB as there is a longer distance they have to travel and I don't know what else could be in the way.
You should also look into useRef(), which will give you access to those input fields without updating your state on every change of the input and thus re-rendering your form over and over.
You can do something like this:
...
const regInput = React.useRef();
...
...
<input
ref={regInput}
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
Then when you're ready to submit, just access the value of the username input like so:
...
const v = regInput.current.value;
...
this is my first time using hooks I don't know How can I clear input fields after submit, form.reset() doesn't work
import { useForm } from "react-hook-form";
import....
export default function AddUser() {
const URL = "http://localhost:3000/AddUser";
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data) => {
if (data) {
axios.post(URL, data);
}
form.reset()
};
here is the return part
return (
<form onSubmit={handleSubmit(onSubmit)} noValidate>
<div className="container">
<input type="text" name="name" placeholder="Name" ref={register({required: true})}/>
<input type="radio" name="gender" value="male" ref={register({ required: true })}/>:Male
<input type="radio" name="gender" value="female" ref={register({ required: true })}/:Female
<button type="submit" className="btn "> add</button>
</div>
</form>
);
}
thanks in advance
//////////
You need to import reset from useForm() hook to be able to use it outside of your tags.
so
const { register, handleSubmit, errors, reset } = useForm();
then on your submit function
const onSubmit = (data) => {
if (data) {
axios.post(URL, data);
}
reset({})
};
Something along those lines should work.
You need to set a default state to set when your click is handle, that way your component will reset on every submit. And yet, and if you wanna prevent default you must set event.preventDefault(); inside the onSubmit function
import { useForm, useState } from "react-hook-form";
import....
export default function AddUser() {
const [formState, setFormState] = useState({})
const URL = "http://localhost:3000/AddUser";
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data) => {
if (data) {
setFormState(data)
axios.post(URL, formState);
}
form.reset()[![enter image description here][1]][1]
};
this is my first week dealing with testing, and i get confused, i'm trying to test SignIn component, i have test the snapshot to ensure that mockup behavior not changing, then i want to test the submit behavior, here is my code:
signIn-component.jsx
import React, { useState } from 'react';
import FormInput from '../form-input/form-input.component';
import CustomButton from '../custom-button/custom-button.component';
import { connect } from 'react-redux';
import {
googleSignInStart,
emailSignInStart,
} from '../../redux/user/user.actions';
import './sign-in.styles.scss';
export const SignIn = ({ emailSignInStart, googleSignInStart }) => {
const [userCredentials, setCredentials] = React.useState({
email: '',
password: '',
});
const { email, password } = userCredentials;
const handleSubmit = async (event) => {
event.preventDefault();
emailSignInStart(email, password);
};
const handleChange = (event) => {
const { value, name } = event.target;
setCredentials({ ...userCredentials, [name]: value });
};
return (
<div className="sign-in">
<h2>I already have an account</h2>
<span>Sign in with your email and password</span>
<form onSubmit={handleSubmit}>
<FormInput
name="email"
type="email"
handleChange={handleChange}
value={email}
label="email"
required
/>
<FormInput
name="password"
type="password"
value={password}
handleChange={handleChange}
label="password"
required
/>
<div className="buttons">
<CustomButton type="submit"> Sign in </CustomButton>
<CustomButton
type="button"
onClick={googleSignInStart}
isGoogleSignIn
>
Sign in with Google
</CustomButton>
</div>
</form>
</div>
);
};
const mapDispatchToProps = (dispatch) => ({
googleSignInStart: () => dispatch(googleSignInStart()),
emailSignInStart: (email, password) =>
dispatch(emailSignInStart({ email, password })),
});
export default connect(null, mapDispatchToProps)(SignIn);
sign.test.js
import { shallow , mount } from 'enzyme';
import React from 'react';
import toJson from 'enzyme-to-json';
import { SignIn } from '../sign-in.component';
describe('Sign In component', () => {
let wrapper;
const mockemailSignInStart = jest.fn();
const mockgoogleSignInStart = jest.fn();
const mockHandleSubmit = jest.fn();
beforeEach(() => {
wrapper = shallow(<SignIn
emailSignInStart={mockemailSignInStart}
googleSignInStart={mockgoogleSignInStart}/>
);
});
it('expect to render signIn component', () => {
expect(toJson(wrapper)).toMatchSnapshot();
});
it('expect call fn on submit', () => {
wrapper.find('form').simulate('submit');
expect(mockHandleSubmit).toBeCalled();
});
});
I have tried mount and render but always expect toBeCalled always return 0
I see 2 problems in your code:
1) I think this:
expect(mockHandleSubmit).toBeCalled();
should actually be
expect(mockemailSignInStart).toBeCalled();
because handleSubmit dispatches emailSignInStart which you mock with googleSignInStart.
2) You should pass some argument to your simulate('submit') or the handleSubmit will throw an error when calling event.preventDefault();. For instance you can just use:
wrapper.find("form").simulate("submit", { preventDefault: jest.fn() });