POST request in react hook form not fetching the user input - reactjs

I am working on an address book and I have fetched all the data from this API url:
https://jsonplaceholder.typicode.com/users
The API cannot really be modified, but it should behave "like if" according to this message in the documentation: "resource will not be really updated on the server but it will be faked as if."
I have set up the react hook form but when I submit my form this is what I get in the dev tools tab network? Shouldn't be showing the user inputs at this point instead of empty string for all fields? The id is the only thing that gets updated.
Is there anything wrong with my Submit function or the actual fetch? Is it ok I have the POST fetch in this component where I have my form as well or should be in the same component where I have the GET request?
Would this one be a good way to approach the POST request?
const NewUserForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = () => {
fetch(URL, {
method: 'POST',
body: JSON.stringify({
id:'',
name: '',
email: '',
address1:'',
address2:'',
city:'',
phone:''
}),
headers: {
'Content-type': 'application/json; charset=UTF-8'
},
})
.then((response) => response.json())
.then((json) => console.log(json));
}
return (
<>
<Header>New user</Header>
<FormContainer>
<Form onSubmit={handleSubmit(onSubmit)}>
<input type="text" placeholder="name" {...register("name", { required: true })} />
{errors.name && <span>This field is required</span>}
<input type="text" placeholder="email" {...register("email", { required: true })} />
{errors.email && <span>This field is required</span>}
<input type="text" placeholder="address1"{...register("address1", { required: true })} />
{errors.address1 && <span>This field is required</span>}
<input type="text" placeholder="address2"{...register("address2", { required: true })} />
{errors.address2 && <span>This field is required</span>}
<input type="text" placeholder="city"{...register("city", { required: true })} />
{errors.city && <span>This field is required</span>}
<input type="text" placeholder="phone"{...register("phone", { required: true })} />
{errors.phone && <span>This field is required</span>}
<input type="submit" />
</Form>
</FormContainer>
</>
);
}

Okay, after checking the react-hook-form docs, here is a possible solution:
In the docs, it says that your onSubmit will have a data param:
const onSubmit = (data) => alert(JSON.stringify(data));
Which means that you can use that in your onSubmit too.
Try changing your onSubmit to use the data parameter:
const onSubmit = (data) => {
fetch(URL, {
method: 'POST',
body: JSON.stringify(data),
And revert the change I suggested earlier regarding handleSubmit. This is correct:
<Form onSubmit={handleSubmit(onSubmit)}>

Related

POST request sending an empty object even though console.log is show input values are being captured

When i submit the form, the object shows up as empty in my db.json file even though console.log is showing that i've capture the inputs.
There are no errors that are popping up, but rather once i hit post, it just shows a new object with an unique ID but nothing that was entered into the form was captured.
import React, { useState } from "react";
const blankNewLegend = {
name:"",
image:"",
nickname:"",
legendType:"",
tactAbility:"",
passAbility:"",
ultAbility:"",
season: 0,
likes: 0,
};
function NewLegendForm() {
const[newLegend, setNewLegend] = useState(blankNewLegend)
console.log(newLegend)
function handleChange(e){
console.log(e)
setNewLegend((prev) => ({
...prev,
[e.target.name] : e.target.value
}
));
e.preventDefault();
}
function handleSubmit(e){
e.preventDefault();
console.log(newLegend)
fetch('http://localhost:6004/legends',{
method: "POST",
header: {
"Content-Type": "application/json"
},
body: JSON.stringify(newLegend),
})
.then((res) => {
console.log(res, 'this')
})
}
return (
<div className="new-legend-form">
<h2>Add New Legend</h2>
<form onSubmit={handleSubmit} >
<input type="text" name="name" placeholder="Legend name" value={newLegend.name} onChange={handleChange}/>
<input type="text" name="image" placeholder="Image URL" value={newLegend.image} onChange={handleChange}/>
<input type="text" name="nickname" placeholder="Nickname" value={newLegend.nickname} onChange={handleChange}/>
<input type="text" name="legendType" placeholder="Legend Type" value={newLegend.legendType} onChange={handleChange}/>
<input type="text" name="tactAbility" placeholder="Tactical Ability" value={newLegend.tactAbility} onChange={handleChange}/>
<input type="text" name="passAbility" placeholder="Passive Ability" value={newLegend.passAbility} onChange={handleChange}/>
<input type="text" name="ultAbility" placeholder="Ultimate Ability" value={newLegend.ultAbility} onChange={handleChange}/>
<input type="number" name="season" placeholder="Debut Season" value={newLegend.season} onChange={handleChange}/>
<input type="number" name="likes" placeholder="Likes" value={newLegend.likes} onChange={handleChange}/>
<button type="submit">Add Legend</button>
</form>
<p>{newLegend.name}</p>
</div>
);
}
export default NewLegendForm;
Your database API can't read the content-type. In your fetch statement, change the key "header" to "headers" (i.e. plural), as shown below. You should then see the full json object in your db.json file:
function handleSubmit(e){
e.preventDefault();
console.log(newLegend)
fetch('http://localhost:6004/legends',{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newLegend),
})
.then((res) => {
console.log(res, 'this')
})
}

"Uncaught TypeError: Cannot read properties of null (reading 'email')"

I'm using Firebase to store some data in users.email. I have some functions in my Javascript code that allow me to read and write the data in the database, and they work fine. However, when I use the same methods in the following code, I get this error:
Uncaught TypeError: Cannot read properties of null (reading 'email') at HTMLDocument
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'addEventListener') at Array.forEach () at He.forEach.
I've tried several options but they don't work and I'm very confused, because the code I use to access the data works fine in other parts of the program. Here user.email sometimes shows a value but most of the time throws an error. Though I already installed Firebase and all other technology, inside of the application other pages work properly. How can i handle this? Is this problem from the database or the client side?
const AddInventory = () => {
const [user] = useAuthState(auth);
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
const url = `http://localhost:5000/item`;
fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(data),
})
.then((res) => res.json())
.then((result) => {
console.log(result);
toast('Add Successfully');
window.location.reload();
});
};
return (
<div className='w-50 mx-auto'>
<h1 className='text-center'>Please add new item</h1>
<form className='d-flex flex-column' onSubmit={handleSubmit(onSubmit)}>
<input
className='mb-2'
placeholder='Item Name'
type='text'
{...register('name', { required: true, maxLength: 20 })}
/>
<textarea
className='mb-2'
placeholder='Description'
type='text'
{...register('details')}
/>
<input
className='mb-2'
placeholder='Price'
type='number'
{...register('price')}
/>
<input
className='mb-2'
placeholder='Quantity'
type='number'
{...register('quantity')}
/>
<input
className='mb-2'
placeholder='Supplier Name'
{...register('suppliername', { required: true, maxLength: 20 })}
/>
<input
className='mb-2'
placeholder='Supplier Email'
value={user?.email}
type='text'
{...register('email')}
readOnly
/>
<input
className='mb-2'
placeholder='Image URL'
type='text'
{...register('img')}
/>
<input className='btn btn-primary' type='submit' value='Add Item' />
</form>
<ToastContainer></ToastContainer>
</div>
);
};
export default AddInventory;

Struggling with select in a form ( and react hooks )

I just do not manage to implement a select option into my form
How can I call this easy select, with my "handleChangeTimezone" thats already working on inputs form ?
working without Select
<form>
<label for="email"> Username : </label>
<input
type="text"
onChange={(e)=>handleChangeUsername(e.target.value)}
/>
<label for="timzone"> Timezone : </label>
<input
type="text"
onChange={(e)=>handleChangeTimezone(e.target.value)}
/>
</form>
with Select this is what it should look like
but says "handleChangeTimezone" is undefined
import Select from 'react-select';
const options = [
{ value: 'london', label: 'London' },
{ value: 'new-york', label: 'New-york' },
{ value: 'tokyo', label: 'Tokyo' },
];
<form>
<label for="email"> Username : </label>
<input
type="text"
onChange={(e)=>handleChangeUsername(e.target.value)}
/>
<label for="timzone"> Timezone : </label>
<Select
onChange={(e) => handleChangeTimezone(e.target.value)}
options={options}
/>
</form>
as requested the full setTimezoneEl plus some fetching working for inputs.
const [timezoneEl, setTimezoneEl] = useState('')
const handleChangeTimezone= e => {
setTimezoneEl(e.target.value)
}
const handleEditProfile = async () => {
const creds = {
user: {
username: usernameEl,
timezone: timezoneEl
}
};
const config = {
method: 'PUT',
mode: 'cors',
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"Authorization": `Bearer ${Cookies.get('token')}`
},
body: JSON.stringify(creds)
};
const res = await fetch(`${process.env.REACT_APP_API_URL}api/users/${id}`, config);
console.log(res)
try {
} catch (err) {
console.log(err)
}
}
also my actual input looks more like this, had to change it a bit for the purpose of the question
<label for="email"> Username : </label>
<input
type="text"
onChange={handleChangeTimezone}}
/>
I'm not sure why you wouldn't just plug in the same handleTimezoneChange function here either by passing it as a prop (can't tell what your component tree is like) or just redefining it.. but the issue here is that you aren't invoking setSelectedOption in the onChange.
Should use a callback like you did with the input form
onChange={(e) => setSelectedOption(e.target.value)}
This is how I resolved my problem :
<select onChange={(e) => setTimezoneEl(e.target.value)} id="timezone">
<option defaultValue hidden>
{getData.data?.attributes?.timezone}
</option>
{timezones.map((option, index) => (
<option value={option.label} key={index}>
{option.label}
</option>
))}
</select>
```

react-hook-form: unregister doesnt clear component value

i have one text field which i am manually unregistering. it is successfully getting unregister and data is excluded from formdata, however user entered value still stays in the text field. i am expecting value also get cleared from component as well. i even tried
setValue('fieldName',"")
is not working. not sure if i am doing something wrong.
so if i re register my text field and trigger validation, you will see required field validation but value is still present in text field
code below:
const App = () => {
const { register, handleSubmit, unregister, errors, setValue } = useForm();
const onSubmit = (data) => {
alert(JSON.stringify(data));
};
useEffect(() => {
register("person.firstName", { required: true });
register("person.lastName", { required: true });
// }
}, [register]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<input
type="text"
name="person.firstName"
onChange={(e) => setValue("person.firstName", e.target.value)}
/>
{errors?.person?.firstName && <p> First name required</p>}
<label>Last Name</label>
<input
type="text"
name="person.lastName"
onChange={(e) => setValue("person.lastName", e.target.value)}
/>
{errors?.person?.lastName && <p> Last name required</p>}
<button
type="button"
onClick={() => {
setValue("person.lastName", "");
unregister("person.lastName");
}}
>
unregister lastName
</button>
<input type="submit" />
</form>
);
};
here is my CSB
i would appreciate any help
the error occurs because you apply your register at useEffect:
useEffect(() => {
register("person.firstName", { required: true });
register("person.lastName", { required: true });
// }
}, [register]);
instead, if you apply to ref at your input fields setValue("person.lastName", "") will clear the field as expected:
<input
ref={register({ required: true })}
type="text"
name="person.firstName"
onChange={(e) => setValue("person.firstName", e.target.value)}
/>

Formik onSubmit - remove form and success message

This is the first time I'm using Formik and I'm facing the following issues:
I created this form using a typescript starter provided in the Formik documentation, and it works, but I'd like to show a success message and remove the form once axios returns with status 200.
So,
1. how do I target the form reference inside of the axios call? normally that is as simple as e.target but the event seems not to be available in Formik.
2. how do I access the state of the form in Formik? to toggle the success message.
The full code is available here: https://codesandbox.io/s/throbbing-water-ffl2w
Thanks a lot in advance.
<Formik
initialValues={{
firstName: "",
lastName: "",
email: ""
}}
// initialStatus={{ // resetForm(); resets this
// sent: "nope"
// }}
onSubmit={(
values: Values,
{ setStatus, setSubmitting, resetForm }: FormikActions<Values>
) => {
axios({
method: "post",
url: "https://getform.io/f/15faef97-5703-4799-930d-c3e698c99967",
data: { email: values.email, values }
}).then(r => {
setSubmitting(false);
setStatus("sent");
//resetForm();
console.log("Thanks!");
});
}}
render={() => (
<Form>
<label htmlFor="firstName">First Name</label>
<Field
id="firstName"
name="firstName"
placeholder="John"
type="text"
/>
<label htmlFor="lastName">Last Name</label>
<Field id="lastName" name="lastName" placeholder="Doe" type="text" />
<label htmlFor="email">Email</label>
<Field
id="email"
name="email"
placeholder="john#acme.com"
type="email"
/>
<button type="submit" style={{ display: "block" }}>
Submit
</button>
</Form>
)}
/>
What I recommend is using state to control what to show in your component (for some reason I cannot save the codesandbox):
const BasicForm: React.FC<{}> = () => {
const [isSent, setIsSent] = React.useState(false);
then, the fetch callback:
.then(r =>
...
setIsSent(true);
Finally in your render function
render={({ isSubmitting, status }) =>
!isSent ?
<Form> ... </Form>:
<div>Success</div>
render is a function that gets props. I see that you use setStatus, so you can get status from props and make changes in Form Component
This is an outdated version of Formik v1.1.2 and I wouldn't recommend to use it as there are some Breaking Changes such as the render method has been deprecated and will be removed in future versions. You may want to use the current version which is v2.1.4
how do I target the form reference inside of the axios call?
Formik passes the values object along with other methods (called FormikBag) inside the onSubmit prop. You can pass these value directly to axios without the need to have your own onSubmit or onChange methods. Please note that <Formik> component has other props. that will give you pretty much full control/access for your needs. That said, I'd recommend to only use Formik state/methods to avoid any side effects or bugs of having the multiple states or handlers.
v2 General Syntax:
<Formik
initialValues={initialValues}
// Other Formik props...
onSubmit = {(Object: form values, Object: Formik Bag ) => {
// access form values...
}}
>
// Access render methods and props (children props)
{(props) => {
return (
<Form>
<Field> ...
</Form>
)
}
}
</Formik>
axios Example:
<Formik
initialValues={initialValues}
onSubmit={(values) => {
console.log(values) // Object holds your form values
axios({
method: "post",
url: "url",
data: { values }
})
})
/>
how do I access the state of the form in Formik? to toggle the success message.
You can use Formik setStatus method from FormikBag inside your onSubmit to pass your server response status, then you can access that status via children props Here is an example:
<Formik
initialValues={initialValues}
onSubmit={(values, setStatus) => {
axios({
method: "post",
url: "url",
data: { values }
})
.then(res => {
if (res.status === 200) {
// 200 means POST method response with success
// Pass your server response to Formik
setStatus({
sent: true,
msg: "Message has been sent! Thanks!"
// Pass more if you need
})
}
})
.catch(err => {
// Something went wrong
setStatus({
sent: false,
msg: `Error! ${err}. Please try again later.`
})
})
})
>
// Later in your code destructuring the children props and use it like so:
{({ status }) => (
<Form>
<Field ... />
{status && status.msg && (
<p className={`alert ${ status.sent ? "alert-success" : "alert-error"}`}>
{status.msg}
</p>
)}
<button>Submit</button>
</Form>
)}
</Formik>
I did fork your codesanbox and updated the dependencies versions/syntax in this codeSandbox Example. Please note that I'm no typescript expert.

Resources