I'm having some trouble pushing the values from my form to an array that I'm mapping on screen.
const ForumTopic = [
{
title: "First Post",
messages: "test",
author: "Dagger",
count: 1,
date: "02/16",
},
];
const [topic, setTopic] = useState(ForumTopic);
Storing ForumTopic in state so I can add entries and display on screen after I click the submit button below.
const addTopic = (e) => {
e.preventDefault();
setTopic([...topic, e.target.value]);
};
<form onSubmit={addTopic}>
Create a topic title
<label htmlFor="title">
<input id="title"></input>
</label>
Write your message
<label htmlFor="message">
<textarea id="message"></textarea>
</label>
<label htmlFor="author">
<input id="author" defaultValue="Dagger" hidden></input>
</label>
<label htmlFor="count">
<input id="count" defaultValue="1" hidden></input>
</label>
<label htmlFor="date">
<input id="date" defaultValue="02/16/2023" hidden></input>
</label>
<button type="submit">
Post New Message
</button>
</form>
That's my code and form. The code is meant to push the values from each label in the form to create a new object inside the topic array. I want everything stored in a new object with the id of each label to match the names of each object (title, author, date, etc) but for some reason all I'm getting are undefined errors.
A simple way to do it is like this.
You need to obtain the value you are getting with an onChange in the input.
LINK to the example: https://stackblitz.com/edit/react-8r9f8l?file=src%2FApp.js
import React, { useState } from 'react';
const ForumTopic = [
{
title: 'First Post',
messages: 'test',
author: 'Dagger',
count: 1,
date: '02/16',
},
];
export default function App() {
const [topic, setTopic] = useState(ForumTopic);
const [inputObj, setInputObj] = useState({
title: '',
messages: '',
author: 'Dagger',
count: 1,
date: '02/16',
});
const handleChange = (event) => {
setInputObj((curr) => ({
...curr,
[event.target.name]: event.target.value,
}));
};
const addTopic = (e) => {
e.preventDefault();
setTopic([...topic, inputObj]);
};
return (
<>
<form onSubmit={addTopic}>
<label htmlFor="title">
Create a topic title
<input
id="title"
name="title"
value={inputObj.title}
onChange={handleChange}
></input>
</label>
<label htmlFor="message">
Write your message
<textarea
id="message"
name="messages"
value={inputObj.messages}
onChange={handleChange}
></textarea>
</label>
<label htmlFor="author">
<input id="author" name="author" defaultValue="Dagger" hidden></input>
</label>
<label htmlFor="count">
<input id="count" name="count" defaultValue="1" hidden></input>
</label>
<label htmlFor="date">
<input id="date" name="date" defaultValue="02/16/2023" hidden></input>
</label>
<button type="submit">Post New Message</button>
</form>
{topic.map((item) => {
return (
<>
<p>{item.title}</p>
<p>{item.messages}</p>
<p>{item.author}</p>
<p>{item.count}</p>
<p>{item.date}</p>
<span>------------</span>
</>
);
})}
</>
);
}
The problem is that on your addTopic function:
e.target.value are always undefined
to access the data you have to do:
const addTopic = (e) => {
e.preventDefault()
const myData = {
title: e.target.title.value,
message: e.target.message.value
}
setTopic(prev => [...prev, myData])
}
Related
So ive got a Booking component which returns a form which lets you choose number of Guests, Time, Occasion and a Date, but i need a way to lift the State up from this component so that i can use it in the future with an API.
I think i dont have the knowledge of the right Syntax or correct way of putting this component and its state. Ive been trying to Code it but the only thing i came up with is this:
import React from 'react'
import { useState } from 'react';
const availableTimes = [
{value: "17:00", text: "17:00"},
{value: "18:00", text: "18:00"},
{value: "19:00", text: "19:00"},
{value: "20:00", text: "20:00"},
{value: "21:00", text: "21:00"},
{value: "22:00", text: "22:00"},
]
const Guests = [
{value: "1", text: "1"},
{value: "2", text: "2"},
{value: "3", text: "3"},
{value: "4", text: "4"},
{value: "5", text: "5"},
{value: "6", text: "6"},
{value: "7", text: "7"},
{value: "8", text: "8"},
{value: "9", text: "9"},
]
const Occasion = [
{value: "Birthday", text: "Birthday"},
{value: "Anniversary", text: "Anniversary"},
]
const Select = ({value, options, onChange}) => {
return(
<select value={value} onChange={onChange}>
{options.map(option => {
return(<option key={option.value} value={option.value}>{option.text}</option>)
})}
</select>
)
}
const Bookingform = () => {
const[resdate, setResdate] = useState("");
const[guests, setGuests] = useState("");
const[occasion, setOccasion] =useState("");
const[restime, setRestime] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
const data = {
Reservation_Time: restime,
Occasion: occasion,
Guests: guests,
Reservation_Date: resdate
}
const json = JSON.stringify(data);
console.clear()
console.log(json)
setResdate("");
setRestime("");
setGuests("");
setOccasion("");
console.log("Form submitted!");
}
return (
<>
<div className='BookingForm'>
<form onSubmit={handleSubmit}>
<fieldset>
<div className='Field'>
<field>
<label htmlFor='res-date'>Choose Date</label>
<input id="res-date" type="date" placeholder='res-date' name='res-date' value={resdate} onChange={(e)=> setResdate(e.target.value)}/>
<label htmlFor='res-time'>Choose time:</label>
<Select id="restime" placeholder="restime" name="restime" value={restime} options={availableTimes} onChange={(e) => setRestime(e.target.value)} />
<label htmlFor="guests">Number of Guests</label>
<Select id="guests" placeholder='guests' name='guests' value={guests} options={Guests} onChange={(e) => setGuests(e.target.value)}/>
<label htmlFor="occasion">Occasion</label>
<Select id="occasion" placeholder='occasion' name="occasion" value={occasion} options={Occasion} onChange={(e) => setOccasion(e.target.value)}/>
</field>
</div>
<button type='submit'>Submit</button>
</fieldset>
</form>
</div>
</>
)
}
export default Bookingform
I think you should read this article:
https://dev.to/smeijer/simple-form-handling-in-react-o72
But a better way than what you have written is using FormData :
function MyForm() {
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
const values = Object.fromEntries(data.entries());
if (!Number(values.age)) {
alert('Your age must be a number');
return;
}
console.log('submitting', values);
};
return (
<form onSubmit={handleSubmit}>
<h1>Hi!</h1>
<p>Enter your name:</p>
<input type="text" name="username" />
<p>Enter your age:</p>
<input type="text" name="age" />
<br /><br />
<input type="submit" />
</form>
);
}
Obviously a much better way would be to create a Hook for yourself using FormData which you can see a full example of it in the article I mentioned above.
So after some more research and reading the mentioned article i came up with this Solution. Is this a valid way of lifting the State of my Component to its Parent?
const formReducer= (state, event) => {
return {
...state,
[event.name]: event.value
}
}
const BookinForm = (props) => {
const [formData, setFormData] = useReducer(formReducer, {});
const [submitting, setSubmitting] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
setSubmitting(true);
props.onSubmit(formData);
setTimeout (() => {
setSubmitting(false);
}, 3000)
};
const handleChange = event => {
const isCheckbox = event.target.type === "checkbox";
setFormData({
name: event.target.name,
value: event.target.value,
value: isCheckbox ? event.target.checked : event.target.value,
});
}
return (
<div className="Wrapper">
<h1>Reserve a Table</h1>
{submitting && <div>You are submitting the following:
<ul>
{Object.entries(formData).map(([name, value]) => (
<li key={name}><strong>{name}</strong>:{value.toString()}</li>
))}
</ul>
</div> }
<form onSubmit={handleSubmit}>
<fieldset>
<label>
<p>Name</p>
<input name="name" onChange={handleChange}/>
</label>
<label>
<p>Email</p>
<input name="email" onChange={handleChange}/>
</label>
<label>
<p>Telephone</p>
<input name="telephone" onChange={handleChange}/>
</label>
</fieldset>
<fieldset>
<label>
<p>Time</p>
<select name="Time" onChange={handleChange}>
<option>17:00</option>
<option>18:00</option>
<option>19:00</option>
<option>20:00</option>
<option>21:00</option>
<option>22:00</option>
</select>
</label>
<label>
<p>Date</p>
<input type="date" name="Time" onChange={handleChange}/>
</label>
<label>
<p>Guests</p>
<input type="number" name="count" min="1" max="9" onChange={handleChange} step="1"/>
</label>
<label>
<p>Choose Occasion</p>
<select>
<option>Anniversary</option>
<option>Birthday</option>
</select>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
)
}
it does function as intended as i can Console log the Data from my Form inside my Parent component if i tell it to use this function On Submit:
const getData = (data) => {
console.log("Coming from Parent", data)
};
Any advice still here or does this code look fine for more experienced people?
i'm a beginner in React and trying to learn useState. and I have difficulties on how to get the value of input and to save the value and print it on button click
const HomePage = () => {
const [state, setState] = useState({
Name: "",
surName: "",
});
const handleChange = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
});
};
const RenderNameOC = () => {
return (
<p>
Halo {Name} {surName}
</p>
);
};
return (
<DivContainer>
<ContainerTitle>
<p>Exercise 2 - Form</p>
</ContainerTitle>
<InputContainer>
<InputArea>
<label>Name: </label>
<input type="text" value={state.Name} onChange={handleChange} />
</InputArea>
<InputArea>
<label>Surname: </label>
<input type="text" value={state.surName} onChange={handleChange} />
</InputArea>
<SubmitButton onClick={RenderNameOC}>Submit</SubmitButton>
</InputContainer>
</DivContainer>
);
};
export default HomePage;
this is my code right now and the error it gave me was 'name' and 'surname' is not defined.
my expected result is that there will be 2 input textbox with for name and surname. and when the button is clicked, it will add a new <p> below it.
Here should be state.Name and state.surName
<p>
Halo {state.Name} {state.surName}
</p>
And add name in both inputs
<input
type="text"
name="Name"
value={state.Name}
onChange={handleChange}
/>
<label>Surname: </label>
<input
type="text"
name="surName"
value={state.surName}
onChange={handleChange}
/>
But no point of returning anything RenderNameOC since onClick is a void function. Just move this template below the submit button
Demo
When I use HTML select it works but it does not work with react-select
Also, the react-select options are arrays that I imported that return an object
Example:
const Program = [
{
value: "MSC",
label: "MSC"
},
{
value: "MCA",
label: "MCA",
isdisabled: true // disable this option
},
{
value: "BSC",
label: "BSC"
},
{
value: "BCA",
label: "BCA",
isdisabled: true // disable this option
}
];
import Select from "react-select";
function Download() {
const requestSelected = e => {
e.preventDefault();
// Axios.post('http://localhost:5000/login', attend)
// .then(res => {
// alert(res.data.message)
// })
console.log(selectedValue);
};
// set value for default selection
const [selectedValue, setSelectedValue] = useState({
Program: "",
Class: "",
Division: "",
Course: "",
Datefrom: "",
Datetill: ""
});
const handleChange = e => {
const { name, value } = e.target;
setSelectedValue({
...selectedValue,
[name]: value
});
};
return (
<form className="D Download" method="POST">
<p>Download Form:</p>
<label className="D_programlabel">Select Program:</label>
<Select
name="Program"
options={Program}
onChange={handleChange}
value={selectedValue.Program}
/>
<label className="D_classlabel">Select Class:</label>
<Select
name="Class"
options={Class}
onChange={handleChange}
value={selectedValue.Class}
/>
<label className="D_divisionlabel">Select Division:</label>
<Select
name="Division"
options={Division}
onChange={handleChange}
value={selectedValue.class}
/>
<label className="D_courselabel">Select Course:</label>
<Select
name="Course"
options={MSCCourses}
onChange={handleChange}
value={selectedValue.Course}
/>
<ul className="date_time-container">
<li>
<label>From Date :</label>
<input
type="date"
name="Datefrom"
placeholder="Enter Date"
onChange={handleChange}
value={selectedValue.Datefrom}
/>
</li>
<li>
<label>Till Date :</label>
<input
type="date"
name="Datetill"
placeholder="Enter Time"
onChange={handleChange}
value={selectedValue.Datetill}
/>
</li>
</ul>
<br />
<ul className="browse_upload-container">
<li>
<label>File :</label>
<input type="file" className="upload" accept=".csv" required></input>
<button type="submit" id="submit-file" class="btn btn-primary">
Download File
</button>
</li>
<li>
<input type="submit" value="Submit" onClick={requestSelected} />
</li>
</ul>
</form>
);
}
export default Download;
onChange return an item of options, so you should add name when call handleChange:
const handleChange = (name, value) => {
setSelectedValue({
...selectedValue,
[name]: value
})
}
onChange={(value) => handleChange("Program", value)}
Trying to get a response when clicking and store the selected option from the selected value genre, not sure how to add a select new state variable or a post method. When the option is clicked the click handler is not called with the selected options so that we can add it to the state. My genre is also seen as undefined when I don't click on the the text type.
Feedback.js
import React, { useState } from "react";
import axios from "axios";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";
import Layout from "./Layout";
const Feedback = () => {
const [values, setValues] = useState({
name: "",
artist: "",
songTitle: "",
email: "",
message: "",
phone: "",
genre: "",
uploadedFiles: [],
buttonText: "Submit",
uploadPhotosButtonText: "Upload files",
});
// destructure state variables
const {
name,
artist,
songTitle,
label,
email,
message,
phone,
genre,
uploadedFiles,
buttonText,
uploadPhotosButtonText,
} = values;
// destructure env variables
const {
REACT_APP_API,
REACT_APP_CLOUDINARY_CLOUD_NAME,
REACT_APP_CLOUDINARY_UPLOAD_SECRET,
} = process.env;
// event handler
const handleChange = (name) => (event) => {
setValues({ ...values, [name]: event.target.value });
};
const handleSubmit = (event) => {
event.preventDefault();
setValues({ ...values, buttonText: "...sending" });
// send to backend for email
console.table({ name, email, phone, message, uploadedFiles, genre });
axios({
method: "POST",
url: `${REACT_APP_API}/feedback`,
data: {
name,
artist,
songTitle,
label,
email,
genre,
phone,
message,
uploadedFiles,
},
})
.then((response) => {
// console.log('feedback submit response', response);
if (response.data.success) toast.success("Thanks for your feedback");
setValues({
...values,
name: "",
artist: "",
songTitle: "",
label: "",
phone: "",
email: "",
genre: "",
message: "",
uploadedFiles: [],
buttonText: "Submitted",
uploadPhotosButtonText: "Uploaded",
});
})
.catch((error) => {
// console.log('feedback submit error', error.response);
if (error.response.data.error) toast.error("Failed! Try again!");
});
};
function onChangeInput(value) {
console.log(value);
}
const uploadWidget = () => {
window.cloudinary.openUploadWidget(
{
cloud_name: REACT_APP_CLOUDINARY_CLOUD_NAME,
upload_preset: REACT_APP_CLOUDINARY_UPLOAD_SECRET,
tags: ["ebooks"],
},
function (error, result) {
// console.log(result);
setValues({
...values,
uploadedFiles: result,
uploadPhotosButtonText: `${
result ? result.length : 0
} Photos uploaded`,
});
}
);
};
const feedbackForm = () => (
<React.Fragment>
<div className="form-group">
<button
onClick={() => uploadWidget()}
className="btn btn-outline-secondary btn-block p-5"
>
{uploadPhotosButtonText}
</button>
</div>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label className="text-muted">Description</label>
<textarea
onChange={handleChange("message")}
type="text"
className="form-control"
value={message}
required
></textarea>
</div>
<div className="form-group">
<label className="text-muted">Your Name</label>
<input
className="form-control"
type="text"
onChange={handleChange("name")}
value={name}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Your Email</label>
<input
className="form-control"
type="email"
onChange={handleChange("email")}
value={email}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Your Phone</label>
<input
className="form-control"
type="number"
onChange={handleChange("phone")}
value={phone}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Artist Name</label>
<input
className="form-control"
type="text"
onChange={handleChange("artist")}
value={artist}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Song Title</label>
<input
className="form-control"
type="text"
onChange={handleChange("songTitle")}
value={songTitle}
required
/>
</div>
<div className="form-group">
<label className="text-muted">Record Label Name</label>
<input
className="form-control"
type="text"
onChange={handleChange("label")}
value={label}
/>
</div>
<div className="form-group">
<label className="text-muted">Music Genre:</label>
<select
className="form-control"
value={genre}
type="select"
onChange={handleChange("genre")}
>
<option value="steak">Steak</option>
<option value="sandwich">Sandwich</option>
<option value="dumpling">Dumpling</option>
</select>
</div>
<button className="btn btn-outline-primary btn-block">
{buttonText}
</button>
</form>
</React.Fragment>
);
return (
<Layout>
<ToastContainer />
<div className="container text-center">
<h1 className="p-5">Feedback Online</h1>
</div>
<div className="container col-md-8 offset-md-2">{feedbackForm()}</div>
<br />
<br />
<br />
</Layout>
);
};
export default Feedback;
I'm getting a really weird return error that on submission I randomly add an extra duplicate field somehow which is of course then undefined. The input value is also randomly copied from one of the other fields within the form.
const GameForm = () => {
const url = 'http://localhost:6060/games'
const handleInputChange = e => {
const { name, value, year, rating, developer } = e.target
setGameData({ ...gameData, [name]: value, [year]: value, [rating]: value, [developer]: value })
}
const onSubmit = (e) => {
e.preventDefault()
const { name, year, rating, developer } = gameData
if (!name || !year || !rating || !developer) return
console.log(gameData)
axios
.post(url, gameData)
.then((res) => {
setGameData(res)
}).catch((error) => console.log(error))
}
const [gameData, setGameData] = useState({ name: '', year: 0, rating: '', developer: "" })
return (
<form onSubmit={onSubmit}>
<label id="name" htmlFor="name">Name: </label>
<input type="text" name="name" placeholder="Game title" onChange={handleInputChange} />
<br />
<label htmlFor="year">Year: </label>
<input type="number" name="year" placeholder="Release year" onChange={handleInputChange} />
<br />
<label htmlFor="rating">Rating: </label>
<input type="text" name="rating" placeholder="Age rating" onChange={handleInputChange} />
<br />
<label htmlFor="developer">Developer: </label>
<input type="text" name="developer" placeholder="Developer" onChange={handleInputChange} />
<br />
<input type="submit" value="Submit"></input>
</form>
)
}
Console logged return: (I also get a 500 error obviously)
{name: "1", year: "1", rating: "1", developer: "1", undefined: "1"}
The undefined value is seemingly taken from any of the existing fields at random.
I feel like I am likely overlooking something obvious.
You are mis-using e.target. It will not have all the properties that you try to destruct from it. From the ones you list in your example code, it will only have name and value, all the other ones (rating, year, developer) will be undefined as they are not part of the event's target property.
The reason you only get one undefined value in your state object is because it keeps overriding itself when you set your state.
The property from the event target you are trying to access is name, so in your case basically e.target.name
Anyway, with that in mind the fix for your app will be quite simple:
const handleInputChange = e => {
const { name, value } = e.target
// Note: The name will hold whatever value of the name prop you put on your input.
// When you are editing the input with the name prop set to name, it will be "name"
// For the input with the name prop set to "year", it will be "year:
// For the input with the name prop set to "developer" it will be "developer" and so on.
setGameData({ ...gameData, [name]: value })
}
Here is a demo for you with the fix:
const App = () => {
const [gameData, setGameData] = React.useState({ name: '', year: 0, rating: '', developer: "" })
const handleInputChange = e => {
const { name, value } = e.target
setGameData({ ...gameData, [name]: value })
}
return (
<div>
<label id="name" htmlFor="name">Name: </label>
<input type="text" name="name" placeholder="Game title" onChange={handleInputChange} />
<br />
<label htmlFor="year">Year: </label>
<input type="number" name="year" placeholder="Release year" onChange={handleInputChange} />
<br />
<label htmlFor="rating">Rating: </label>
<input type="text" name="rating" placeholder="Age rating" onChange={handleInputChange} />
<br />
<label htmlFor="developer">Developer: </label>
<input type="text" name="developer" placeholder="Developer" onChange={handleInputChange} />
<br />
<button onClick={() => console.warn('GAME DATA OBJECT', gameData)}>Click</button>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>