add multiple input lines and add it to fetch method with reactJS - javascript

I want to add some input lines by a click of a button and add it to state so I can send it to the server, but I'm not sure how to add it to the fetch method or even if it's added to state,
this is what i have so far:
export class AdminPage extends React.Component {
constructor(props){
super(props);
this.state = {
sendeEmail: '',
matrialeliste: [{
matrialer: '',
antal: '',
pris: ''}]
};
}
handleUserInput = (e) => {
if (["matrialer", "antal", "pris"].includes(e.target.className) ) {
let matrialeliste = [...this.state.matrialeliste]
//matrialeliste[e.target.dataset.id][e.target.className] = e.target.value
this.setState({ matrialeliste }, () => console.log(this.state.matrialeliste))
} else {
const name = e.target.name;
const value = e.target.value;
this.setState({[name]: value};
}
}
addMatrialeliste = (e) => {
this.setState((prevState) => ({
matrialeliste: [...prevState.matrialeliste, {matrialer:"", antal:"", pris:""}],
}));
}
onSubmitSignIn = (event) => {
event.preventDefault();
fetch(`${api.url}/form`, {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
sendeEmail: this.state.sendeEmail,
})
})
.then((response) => (response.json()))
.catch(error => console.log(error));
}
render(){
let {matrialeliste} = this.state;
return(
<div>
<div>
<h1>Arbejds seddel</h1>
<form>
<div>
<button type="button" onClick={this.addMatrialeliste}>
tilføj materialer
</button>
{
matrialeliste.map((val, idx) => {
return(
<div key={idx}>
<div>
<label htmlFor="matrialer">
Matrialeliste
</label>
<input name='matrialer' type="text" className='matrialer' onChange={this.handleUserInput} />
</div>
<div>
<label htmlFor="antal">
Antal
</label>
<input name='antal' type="number" className='antal' onChange={this.handleUserInput} />
</div>
<div>
<label htmlFor="pris">
Pris
</label>
<input name='pris' type="number" className='pris' onChange={this.handleUserInput} />
</div>
</div>)})}
<label htmlFor="email">
E-mail
</label>
<input name='email' type="email" onChange={e => this.handleUserInput} />
<button type="submit">Send som E-mail</button>
<div>
<button type="submit" disabled=this.state.formValid}>Create</button>
</div>
</div>
</form>
</div>
</div>
);
}
}
I can get to add extra lines, but I don't know how to add it to the fetch method.
I was thinking I could map it, but I'm still unsure how do that

Creating forms with plain React requires you to write each part of the process and for a complex state its become tough .so, my personal opinion is to go with Formik or React Hook Form as they cover most of the features.
In your case, I am assuming you want to sent whole state to fetch method .here is an example of your code which implemented with Formik library.

Related

Need to add one input field in file upload component

I have one component in which we have file upload facility. I need to add one additioonal input filed so when user clicks on the upload button one input filed and one file should be sent to server.
since its class component I am not able to use hook. its legacy application.
import axios from 'axios';
import React,{Component} from 'react';
class App extends Component {
state = {
// Initially, no file is selected
selectedFile: null
};
// On file select (from the pop up)
onFileChange = event => {
// Update the state
this.setState({ selectedFile: event.target.files[0] });
};
// On file upload (click the upload button)
onFileUpload = () => {
// Create an object of formData
const formData = new FormData();
// Update the formData object
formData.append(
"myFile",
this.state.selectedFile,
this.state.selectedFile.name
);
// Details of the uploaded file
console.log(this.state.selectedFile);
// Request made to the backend api
// Send formData object
axios.post("api/uploadfile", formData);
};
// File content to be displayed after
// file upload is complete
fileData = () => {
if (this.state.selectedFile) {
return (
<div>
<h2>File Details:</h2>
<p>File Name: {this.state.selectedFile.name}</p>
<p>File Type: {this.state.selectedFile.type}</p>
<p>
Last Modified:{" "}
{this.state.selectedFile.lastModifiedDate.toDateString()}
</p>
</div>
);
} else {
return (
<div>
<br />
<h4>Choose before Pressing the Upload button</h4>
</div>
);
}
};
render() {
return (
<div>
<h3>
File Upload using React!
</h3>
<div>
<input type="file" onChange={this.onFileChange} />
<button onClick={this.onFileUpload}>
Upload!
</button>
</div>
{this.fileData()}
</div>
);
}
}
export default App;
I tried a lot but it is not working properly. if you need I can put the modified code. since its quite messy I put only working code without input field.
Could you please help me to add one input field, please.
Edit 1
Modified Code
import React from 'react';
import axios from 'axios';
class FileUpload extends React.Component {
constructor() {
super();
this.state = {
selectedFile: '',
countryCode: '',
responseArray: [],
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInput = this.handleInput.bind(this);
}
handleInputChange(event) {
this.setState({
selectedFile: event.target.value,
responseArray: [],
});
}
handleInput(event) {
this.setState({
countryCode: event.target.value,
});
}
handleSubmit() {
if (!this.state.selectedFile) {
alert('Please select The file');
return false;
}
if (!this.state.countryCode) {
alert('Please select The Country Code');
return false;
}
const data = new FormData();
for (let i = 0; i < this.state.selectedFile.length; i++) {
data.append('file', this.state.selectedFile[i]);
}
data.append('countryCode', this.state.countryCode);
console.log(data.countryCode);
let url = process.env.API_URL;
axios.post('http://localhost:8080/file_upload', data, {}).then(
(res) => {
console.log(data);
// this.setState({ responseArray: res.data });
// this.resetFile();
},
(error) => {
alert(error);
}
);
}
resetFile() {
document.getElementsByName('file')[0].value = null;
}
render() {
return (
<form>
<div className="row">
<div className="col-md-12">
<h1>Translation File Upload</h1>
<div className="form-row">
<div className="form-group col-md-8">
<label>Please enter the country code</label>
<input
type="text"
value={this.state.countryCode}
onChange={this.handleInput}
required
/>
</div>
</div>
<div className="form-row">
<div className="form-group col-md-8">
<label>Select File :</label>
<input
type="file"
className="form-control"
multiple
name="file"
onChange={this.handleInputChange}
required
/>
<hr />
</div>
</div>
<br />
<div className="form-row">
<div className="col-md-6">
<button onClick={this.handleSubmit.bind(this)}>Upload </button>
</div>
</div>
<br />
</div>
</div>
</form>
);
}
}
export default FileUpload;
Can you try
<h3>
File Upload using React!
</h3>
<div>
<input type="file" onChange={this.onFileChange} />
<button onClick={this.onFileUpload}>
Upload!
</button>
<input type="text" onChange={this.onInputChange} required>
</div>
and then in your code
inputField: ''
onInputChange = event => {
// Update the state
this.setState({ inputField: event.target.value });
};
// in the formData part
formData.append(
"inputField",
this.state.inputField
);

i cant use the map function TypeError: Cannot read property 'map' of undefined

i just enter to "REACT " world to do my front-end off my own project and i have problem for 2 day with the function map , i get data from my back-end and i just save the id in the Cuartos array , i dont know what its my error , i try it with for loop with console.log in a function out of render and it work , but work out of the render function how i can resolve it ? i need to get all the cuarto id in the render
this is my code
class FormInterruptor extends React.Component {
constructor (){
super();
const axx={au:[]};
const Cuartos = [];
axios.get("http://localhost:5000/API/Cuartos")
.then(response => {
const a=JSON.stringify(response.data);
console.log(response.data);
axx.au=response.data;
const b=JSON.stringify(axx.au.idcuarto);
console.log("aqui estas" )
for (let i = 1; i < b.length; i=i+2)
{
Cuartos.push({idcuarto:parseInt((JSON.stringify(axx.au.idcuarto))[i])});
}
});
this.state = {
IdInterruptor: '',
IdCuarto: '',
Pin: '',
Dimmer: '',
Cuartos
};
}
onChange(e){
this.setState({
[e.target.name]:e.target.value
});
}
handleSubmit = event => {
event.preventDefault();
const Luz = {
IdInterruptor: this.state.IdInterruptor,
IdCuarto: this.state.IdCuarto,
Pin: this.state.Pin,
Dimmer: this.state.Dimmer
};
//AYUDA CON EL LUGAR DODNE SE PONDRA EL INTERRUPTOR
let config = {headers: {'Access-Control-Allow-Origin': "*"}};
axios.post('http://localhost:5000/API/Cuarto/1/Luz/add', Luz , config)
.then(res => {
//console.log(res);
console.log(res.data);
})
}
render(){
return (
<div className="App">
<header className="App-header">
<img src={process.env.PUBLIC_URL + '/Images/Escudo.png'} alt='Escudo' width='400'/>
<div className="Formulario">
<h2>
Formulario Luz
</h2>
<form onSubmit={this.handleSubmit} >
<div id='form'>
<input id="form" type="text"placeholder="ID del interruptor" value={this.state.IdInterruptor} name="IdInterruptor" onChange={this.onChange.bind(this)} />
</div>
<div id="separador">
<select id="form" name="IdCuarto" value={this.state.IdCuarto} onChange={this.onChange.bind(this)} >
</select>
</div>
<div id="separador">
<input id="form" type="text" name="Pin" placeholder="Pin" value={this.state.Pin} onChange={this.onChange.bind(this)} />
</div>
<div id="separador">
<input id="form" type="text" name="Dimmer" placeholder ="Dimmer" value={this.state.Dimmer} onChange={this.onChange.bind(this)}/>
</div>
<div >
<input type="submit" value="Submit" className="button" onChange={this.onChange}/>
</div>
</form>
</div>
<div>
{this.state.Cuartos.map(p => {
return <p > {p.idcuarto} !</p>}
)}
</div>
</header>
</div>
);
}
}
export default FormInterruptor
Update:
i change the code and i change my state in the componentDidMount and
this is my data array of Cuartos how i need to use the map function
enter image description here
First and foremost, what you should be doing is to make the HTTP request in the ComponentDidMount lifecycle hook instead of the constructor, as the purpose of the constructor is only for
Initializing local state by assigning an object to this.state. Binding
event handler methods to an instance.
constructor(props) {
super(props);
this.state = {
IdInterruptor: '',
IdCuarto: '',
Pin: '',
Dimmer: '',
Cuartos: undefined,
}
}
componentDidMount() {
let Cuartos;
axios.get("http://localhost:5000/API/Cuartos")
.then(response => {
const a=JSON.stringify(response.data);
console.log(response.data);
axx.au=response.data;
const b=JSON.stringify(axx.au.idcuarto);
console.log("aqui estas" )
for (let i = 1; i < b.length; i=i+2) {
Cuartos.push({idcuarto:parseInt((JSON.stringify(axx.au.idcuarto))[i])});
}
this.setState({ Cuartos });
});
}
Then, on your render, you should carry out a check such that you will only carry out Array.map() when the request is returned, and that Cuartos is defined, and the idcuartos array is not empty.
render() {
const { Cuartos } = this.state;
return <>
<div>
{
Cuartos && Cuartos.idcuartos.length && Cuartos.idcuartos.map(p => {
return . <p>{q}</p>
})
}
</div>
</>
}
Make your api call in componentDidMount and save your data to state and then manipulate your data before rendering.
class FormInterruptor extends React.Component {
constructor (){
super();
this.state = {
IdInterruptor: '',
IdCuarto: '',
Pin: '',
Dimmer: '',
Cuartos:[]
};
}
componentDidMount(){
axios.get("http://localhost:5000/API/Cuartos")
.then(response => this.setState({Cuartos:res.data}));
}
onChange(e){
this.setState({
[e.target.name]:e.target.value
});
}
handleSubmit = event => {
event.preventDefault();
const Luz = {
IdInterruptor: this.state.IdInterruptor,
IdCuarto: this.state.IdCuarto,
Pin: this.state.Pin,
Dimmer: this.state.Dimmer
};
//AYUDA CON EL LUGAR DODNE SE PONDRA EL INTERRUPTOR
let config = {headers: {'Access-Control-Allow-Origin': "*"}};
axios.post('http://localhost:5000/API/Cuarto/1/Luz/add', Luz , config)
.then(res => {
//console.log(res);
console.log(res.data);
})
}
render(){
return (
<div className="App">
<header className="App-header">
<img src={process.env.PUBLIC_URL + '/Images/Escudo.png'} alt='Escudo' width='400'/>
<div className="Formulario">
<h2>
Formulario Luz
</h2>
<form onSubmit={this.handleSubmit} >
<div id='form'>
<input id="form" type="text"placeholder="ID del interruptor" value={this.state.IdInterruptor} name="IdInterruptor" onChange={this.onChange.bind(this)} />
</div>
<div id="separador">
<select id="form" name="IdCuarto" value={this.state.IdCuarto} onChange={this.onChange.bind(this)} >
</select>
</div>
<div id="separador">
<input id="form" type="text" name="Pin" placeholder="Pin" value={this.state.Pin} onChange={this.onChange.bind(this)} />
</div>
<div id="separador">
<input id="form" type="text" name="Dimmer" placeholder ="Dimmer" value={this.state.Dimmer} onChange={this.onChange.bind(this)}/>
</div>
<div >
<input type="submit" value="Submit" className="button" onChange={this.onChange}/>
</div>
</form>
</div>
<div>
{this.state.Cuartos.map(p => {
return <p > {p.idcuarto} !</p>}
)}
</div>
</header>
</div>
);
}
}
export default FormInterruptor
You are making a call in the constructor which is not recommended, try doing it in componentDidMount
Javascript is asynchronous so when are making a call using axios it will not wait until the response got back it will continue to render the component, you need to update your component after you got the response
If you still want to render with your existing code, add below line after the for loop inside the axios call back
this.setState({"Cuartos": Cuartos})

React: generating input field data with a button component

I'm creating an intake form where a piece of data can be input into the text field, or generated randomly with a button next to the field.
I want to do this for 3 fields on the form so I created a component
called <RandomDataButton />
I'm stuck with how to make sure the results of the calculation done by the button component update the value of the text box so that the form submission contains the generated data.
I don't fully understand the state propagation, but what I do understand is that the flow is one way, down the hierarchy of components.
So what I am attempting to do is have a choice of inputting some data in the text box, or generating some random data from a button (I'd like to reuse it in other ui creations)
Where I am stuck is how do I update the input field from the componenet that is lower in the hierarchy.
Do I pass the state to the randomizer button and then have it update a copy of state? Or am I totally off base with that approach?
App:
class App extends React.Component {
render(){
return (
<div>
<DataInputForm />
</div>
);
}
}
DataInputForm:
class DataInputForm extends React.Component{
state= {
projectname: '',
datasource: '',
data1: '',
data2: '',
data3: '',
};
handleSubmit = e => {
e.preventDefault();
console.log({
projectname: this.projectname.value,
datasource: this.datasource.value,
data1: this.data1.value,
data2: this.data2.value,
data3: this.data3.value,
});
}
handleChange = e => this.setState({[e.target.name]: e.target.value});
render(){
return(
<form className="ui form" onSubmit={this.handleSubmit}>
<div className="field">
<label htmlFor="projectname">Project Name: </label>
<input
type="text"
id="projectname"
name="projectname"
placeholder="Project Name"
ref={input => this.projectname = input}
/>
</div>
<div className="field">
<label htmlFor="datasource">Data Source: </label>
<input
type="text"
id="datrasource"
name="datasource"
placeholder="Data Source"
ref={input => this.datasource = input}
/>
</div>
<div className="field">
<label htmlFor="data1">Data 1: </label>
<input
type="number"
min="3"
max="18"
id="data1"
name="data1"
ref={input => this.data1 = input}
/>
<RandomDataButton buttonid={"data1button"} buttonname={"Data1"} />
</div>
<div className="field">
<label htmlFor="data2">Data 2: </label>
<input
type="number"
min="3"
max="18"
id="data2"
name="data2"
ref={input => this.data2 = input}
/>
<RandomDataButton buttonid={"data2button"} buttonname={"Data2"} />
</div>
<div className="field">
<label htmlFor="data3">Data 3: </label>
<input
type="number"
min="3"
max="18"
id="data3"
name="data3"
ref={input => this.data3 = input}
/>
<RandomDataButton buttonid={"data3button"} buttonname={"Data3"} />
</div>
<button className="ui button" type="submit">Create Data</button>
</form>
);
}
}
RandomDataButton:
const getRandom = max => Math.floor(Math.random() * Math.floor(max));
class RandomDataButton extends React.Component {
generateData(value){
var result, destination;
destination = value.toLowerCase();
result = getRandom(1000);
console.log("Generated " + result + " for range of " + value + "]: " + destination);
//this.state.{destination};
}
render(){
return(
<button id={this.props.buttonid} type="button" onClick={this.generateData.bind(null,this.props.buttonname)}>{this.props.buttonname}</button>
//<button id="strbutton" type="button">Generate</button>
);
}
}
Pass a function as prop to RandomDataButton. Define the function in DataInputForm and use it update the state in DataInputForm by calling the prop function from RandomDataButton whenever you need the update.
It seems you are working with multiple components, so while working with multiple components, it is highly recommended to use any central storage container, which would be useful to get your desired data in any components
Flux and redux both are tested architectures for data state management, you could use any of them, I would recommend using redux.
Here's a codesandbox for your reference: https://codesandbox.io/s/bold-frog-01ff2
This is effectively a continuation of Amala's suggestion.
You are correct, the hierarchy is one-way. Which means we should define a function in DataInputForm (lvl2) and pass it as a prop to RandomDataButton (lvl3). That function is bound to DataInputForm's execution context, and we want to update it's state so we can feed the new data back into each individual input.
For example:
createRandomText = (associatedField, value) => {
this.setState(
{
[associatedField]: value
},
() => console.log(this.state)
);
};
So to update the state correctly, we need to provide a field corresponding to the right input, and a value (the randomized value).
We pass in that function as a prop to RandomDataButton and use it for the onClick() handler.
class RandomDataButton extends React.Component {
generateData = () => {
let result = getRandom(1000);
this.props.createRandomText(this.props.matchingInput, result);
};
render() {
return (
<button
id={this.props.buttonid}
type="button"
onClick={this.generateData}
>
{this.props.buttonname}
</button>
//<button id="strbutton" type="button">Generate</button>
);
}
}
Additionally we need to provide another prop to the button component so we can call the above function correctly:
<RandomDataButton
buttonid={"data1button"}
buttonname={"Data1"}
createRandomText={this.createRandomText}
matchingInput={"data1"}
/>
See sandbox for full details :)

This handleSubmit() is not working when I move my Form to a different file

I am following the Scrimba tutorial on React but I decided to move my Form to a new file/component and change the functions to ES6.
Can someone tell me why? Thanks!
Now the handle Submit is not working (it works when the form is rendered in Meme Generator) but I don't know why and it doesn't throw any errors.
import React, { Component } from 'react'
import Form from "./Form"
class MemeGenerator extends Component {
constructor() {
super()
this.state = {
topText: "",
bottomText: "",
randomImg: "http://i.imgflip.com/1bij.jpg",
allMemeImgs: []
}
}
componentDidMount() {
fetch("https://api.imgflip.com/get_memes").then(response => response.json())
.then(response => {
const {memes} =response.data
console.log(memes[2])
this.setState({allMemeImgs: memes})
})
}
handleChange = (event) => {
const {name, value} = event.target
this.setState({[name]: value})
}
handleSubmit = (event) => {
event.preventDefault()
const randNum = Math.floor(Math.random() *
this.state.allMemeImgs.length)
const randMemeImg = this.state.allMemeImgs[randNum].url
this.setState({ randomImg: randMemeImg})
}
render() {
return (
<Form
handleChange = {this.handleChange}
data={this.state}
onSubmit={this.handleSubmit}
/>
)
}
}
export default MemeGenerator
The image is supposed to update to a random image every time the button is clicked. But it doesn't, also the whole page reloads, ignoring the event prevent Default
import React from 'react'
import style from './styles.module.css'
function Form(props) {
return (
<div>
<form className={style.memeForm} onSubmit={props.handleSubmit}>
<input
type="text"
placeholder="Type your top text"
name="topText"
value={props.data.topText}
onChange={props.handleChange}
/>
<input
type="text"
placeholder="Type your bottom text"
name="bottomText"
value={props.data.bottomText}
onChange={props.handleChange}
/>
<button>Generate</button>
</form>
<div className={style.meme}>
<img src={props.data.randomImg} alt="" />
<h2 className={style.top}>{props.data.topText}</h2>
<h2 className={style.bottom}>{props.data.bottomText}</h2>
</div>
</div>
)
}
export default Form
change these lines of code
onSubmit={(event) => props.handleSubmit(event)}
and
<button type='submit'>Generate</button>
<form className={style.memeForm} onSubmit={(event) => props.handleSubmit(event)}>
<input
type='text'
placeholder='Type your top text'
name='topText'
value={props.data.topText}
onChange={props.handleChange}
/>
<input
type='text'
placeholder='Type your bottom text'
name='bottomText'
value={props.data.bottomText}
onChange={props.handleChange}
/>
<button type='submit'>Generate</button>
</form>;

Unable to type into React input field

I am unable to type any input into my input field. I am using React, and have already set a handleChange and a handleSubmit function. The first two input fields, for 'name' and 'email', take input just fine. But for 'favoriteCity', it doesn't seem to work.
I am wondering if it is due to a MongoDB error that I am getting.
class UserPage extends Component {
state = {
user: [],
newUser: {
name: '',
email: '',
favoriteCity: ''
}
}
getAllUsers = () => {
axios.get('/api/users')
.then(res => {
this.setState({ user: res.data })
})
}
componentDidMount() {
this.getAllUsers()
}
handleChange = event => {
const newUser = { ...this.state.newUser };
newUser[event.target.name] = event.target.value;
this.setState({ newUser: newUser});
}
handleSubmit = event => {
event.preventDefault()
axios.post('/api/users', this.state.newUser)
.then(res => {
this.props.history.push(`/users/${res.data._id}`)
})
}
render() {
return (
<div>
{ /* This shows a list of All Users */ }
{this.state.user.map(user => (
<div key={user._id}>
<Link to={`/users/${user._id}`}>{user.name}</Link>
</div>
))}
<h1>New User Page</h1>
<form onSubmit={this.handleSubmit}>
<label>Name: </label>
<input
type="text"
name="name"
placeholder="Name?"
value={this.state.newUser.name}
onChange={this.handleChange}
/>
<label>Email: </label>
<input
type="text"
name="email"
placeholder="Email?"
value={this.state.newUser.email}
onChange={this.handleChange}
/>
<label>Favorite City: </label>
<input
type="text"
name="city"
placeholder="Favorite City?"
value={this.state.newUser.favoriteCity}
onChange={this.handleChange}
/>
<Button
type="submit"
value="Submit"
variant="contained"
color="primary"
>
Create User
</Button>
</form>
</div>
);
}
}
export default UserPage;
Please help.
Weird that email works fine, from what you posted your handleChange function is only updating the name on the newUser.
What you should see is what you type in all the inputs appear in the name input.
To fix this, you should probably have separate change handlers for each input:
handleNameChange
handleEmailChange
...
You should also consider storing name, email etc.. at the root of your state instead of nesting them in an object, that'll simplify the handler functions code.

Categories

Resources