Struggling with select in a form ( and react hooks ) - reactjs

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>
```

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')
})
}

ReactJS .... map() is not a function

I have a problem with my code. I have a tag with options, when I try to set contact type set contact type appears white screen with error in the console "types.map() is not a function" white screen with errors. I think the problem comes from that at the beginning "types" are array and when I choose contact type with the state() "types" become a single value. I don't know how to fix that.
This is my code:
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import styles from './postContact.module.css';
const PostContact = () => {
const [nickName, setNickName] = useState('');
const [address, setAddress] = useState('');
const [phoneNumber, setPhoneNumber] = useState('');
const [account, setAccount] = useState('');
const [types, setType] = useState([]);
const navigate = useNavigate();
const postContact = async (e) => {
e.preventDefault();
await fetch('http://localhost:5090/api/contacts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
nickName,
address,
phoneNumber,
userId: 1,
contactTypeId: types,
account
})
})
.then(() => {
navigate('/');
})
.catch((e) => {
alert(e.message)
});
};
const getTypes = async () => {
const request = await fetch('http://localhost:5090/api/contactTypes', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const response = await request.json();
setType(response);
};
useEffect(() => {
getTypes();
}, []);
return (
<form className={styles['post-form']} onSubmit={postContact}>
<label htmlFor='nickName'>Nickname</label>
<input className={styles.input} id='nickName' type='text' onChange={e => setNickName(e.target.value)} value={nickName} />
<label htmlFor='address'>Address</label>
<textarea className={styles.input} id='address' type='text' onChange={e => setAddress(e.target.value)} value={address} />
<label htmlFor='phoneNumber'>Phone Number</label>
<input className={styles.input} id='phoneNumber' type='text' onChange={e => setPhoneNumber(e.target.value)} value={phoneNumber} />
<label htmlFor='account'>Account</label>
<input className={styles.input} id='account' type='text' onChange={e => setAccount(e.target.value)} value={account} />
<label htmlFor="type">Contact Type</label>
<select className={styles.input} title="type" name="type" onChange={e => setType(e.target.value)} value={types}>
{types.map(type=>
<option key={type.id} value={type.id}>{type.type}</option>
)}
</select>
<button className="btn btn-primary mt-5" type='submit' name='Post'>Create</button>
</form>
);
};
export default PostContact;
I'll be grateful if anyone can help me.
On the first render the value for types will be undefined ( on sync code execution ), try using it as
<select className={styles.input} title="type" name="type" onChange={e => setType(e.target.value)} value={types}>
{types?.map(type=>
<option key={type.id} value={type.id}>{type.type}</option>
)}
</select>
? will make sure to run map once value for types is there ( also make sure it is mappable ( is an array ))
Handle the array falsy cases before doing any operations on it
<select
className={styles.input}
title="type" name="type"
onChange={e => setType(e.target.value)}
value={types}
>
{types.length && types.map(type=>
<option key={type.id} value={type.id}>{type.type}</option>
)}
</select>

How to POST to server / I keep getting a post 400 error

I'm sure I'm missing something but I can't seem to figure out what it is. I'm trying to POST the dropdown menu but it doesn't show up in the console and I get a post 400 when I submit. Any help would be greatly appreciated. thank you <3
Sorry it's a lot of code but I'm still navigating through react and just need some help on this.
Also I have React, useState, and useEffect imported from "react" it's just not showing up on the code
import axios from "axios";
function PostForm() {
const url = "https://frontend-take-home.fetchrewards.com/form";
const [data, setData] = useState({
name: "",
email: "",
password: "",
occupations: "",
states: "",
});
const URL = "https://frontend-take-home.fetchrewards.com/form";
const [occupations, setOccupation] = useState([]);
const [states, setState] = useState([]);
useEffect(function () {
axios
.get(URL)
.then((data) => setOccupation(data.data.occupations))
.catch((error) => console.log(error));
axios
.get(URL)
.then((data) => setState(data.data.states))
.catch((error) => console.log(error));
}, []);
function handle(e) {
const newData = { ...data };
newData[e.target.id] = e.target.value;
setData(newData);
console.log(newData);
}
function submit(e) {
e.preventDefault();
axios.post(url, data).then((res) => {
console.log(res.data);
});
}
return (
<div>
<form onSubmit={(e) => submit(e)}>
<label>Full Name</label>
<input
onChange={(e) => handle(e)}
id="name"
value={data.name}
placeholder="Full Name"
type="text"
name="name"
/>
<label>Email</label>
<input
onChange={(e) => handle(e)}
id="email"
value={data.email}
placeholder="Email"
type="text"
name="email"
/>
<label>Password</label>
<input
onChange={(e) => handle(e)}
id="password"
value={data.password}
placeholder="Password"
type="password"
name="password"
/>
<div>
<select>
{occupations.map((occupation) => (
<option
key={occupation}
id="occupation"
name="occupations"
placeholder="Occupation"
onChange={(e) => handle(e)}
value={data.occupations}
>
{occupation}
</option>
))}
</select>
<select>
{states.map((state) => (
<option
key={state.id}
value={data.states}
onChange={(e) => handle(e)}
>
{state.name}({state.abbreviation})
</option>
))}
</select>
</div>
<button type="submit">Sign Up</button>
</form>
</div>
);
}
export default PostForm;
It could be your server error which you are trying to request which means your code is correct , try to provide the full error message for more specific answer

POST request in react hook form not fetching the user input

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

React: capturing form values that were not changed

In the tutorials I've found, the best way to capture forms inputs, is to use a "onchange" handler.
However, how should we deal with fields that are left to the default value ?
For example in something like the example below, if "country" is left to the default value, the "formData" will never get it.
const [formData, setFormData] = useReducer(formReducer, {});
const handleChange = event => {
setFormData({
name: event.target.name,
value: event.target.value,
});
}
const handleSubmit = event => {
event.preventDefault();
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
};
fetch('http://0.0.0.0:8000/add_user/', requestOptions)
}
<form onSubmit={handleSubmit}>
<fieldset>
<input type="text" name="name" placeholder="Name" onChange={handleChange} />
<input type="text" name="country" value="" onChange={handleChange} required />
</fieldset>
<Button type="submit">Submit</Button>
</form>
I've thought about some solutions but I don't think they are the best practices. For example, adding a snippet of code that manually assigns all default values to the form in the construction, so the formData is pre-filled.
Is there a clean & nice way to do it ?
In your useReducer hook you should store initial country value and then assign it to the value in country input, like below:
const [formData, setFormData] = useReducer(formReducer, { country: "" });
<form onSubmit={handleSubmit}>
<fieldset>
<input type="text" name="name" placeholder="Name" onChange={handleChange} />
<input type="text" name="country" value={formData.country} onChange={handleChange} required />
</fieldset>
<Button type="submit">Submit</Button>
</form>

Resources