Reputation:
Here is something like a CRUD using React and axios. I have some component with form and inputs. The form recieved data from express backend using axios GET method, and semingly there is no problems. But the second task is to change data in inputs and post to the route using axios POST. For some reason it fails to collect the data from all inputs to json for the POST request. Someone tell me what is wrong here?
The JSON requested from backend looks like this:
{
"data": [
{
"_id": "5d28a6fcec97b111c2f5867d",
"phone": "+1 (111) 111 11 11",
"email": "[email protected]",
"title": "khkjhkjhkj",
"longTitle": "lkjlkjlkjlk",
"introTitle": "Shutruk",
"introLongTitle": "Shutruk-Nahhunte",
"videoLink": "khkjhkjhkj",
"introText": "lkjlkjlkjlk",
"__v": 0
}
]
}
Here is the component:
import React, { Component } from 'react';
import axios from 'axios';
class Misc extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
value: [],
loading: true,
error: false,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
onChange(e) {
this.setState({ value: e.target.value });
}
componentDidMount() {
axios.get('http://localhost:5555/data')
.then(res => {
const data = res.data.data; // get the data array instead of object
this.setState({ data, loading: false });
})
.catch(err => { // log request error and prevent access to undefined state
this.setState({ loading: false, error: true });
console.error(err);
})
}
handleSubmit(e) {
e.preventDefault();
const data = {
value: this.state.value
};
axios.post('http://localhost:5555/data', { data })
.then(res => {
console.log(data);
})
}
render() {
if (this.state.loading) {
return(
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.data[0]) { // if request failed or data is empty don't try to access it either
return(
<div>
<p> An error occured </p>
</div>
)
}
return (
<form action="" onSubmit={this.handleSubmit}>
<h2 className="center" >Change values</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn"/></div>
<h5>Phone:</h5>
<input type="text" name="phone" defaultValue={ this.state.data[0].phone } onChange={e => this.onChange(e)} />
<h5>Email:</h5>
<input type="text" name="email" defaultValue={ this.state.data[0].email } onChange={e => this.onChange(e)} />
<h5>Title:</h5>
<input type="text" name="title" defaultValue={ this.state.data[0].title } onChange={e => this.onChange(e)} />
<h5>Description:</h5>
<input type="text" name="longTitle" defaultValue={ this.state.data[0].longTitle } onChange={e => this.onChange(e)} />
<h2 className="center" >Intro:</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn"/></div>
<h5>Title:</h5>
<input type="text" name="introTitle" defaultValue={ this.state.data[0].introTitle } onChange={e => this.onChange(e)} />
<h5>Description:</h5>
<input type="text" name="introLongTitle" defaultValue={ this.state.data[0].introLongTitle } onChange={e => this.onChange(e)} />
<h5>Link to video:</h5>
<input type="text" name="videoLink" defaultValue={ this.state.data[0].videoLink } onChange={e => this.onChange(e)} />
<h5>Text:</h5>
<textarea name="introText" id="" cols="30" rows="10" defaultValue={ this.state.data[0].introText } onChange={e => this.onChange(e)}></textarea>
<button type="submit" className="btn-large waves-effect waves-light xbutton">Save</button>
</form>
);
}
}
export default Misc;
Upvotes: 0
Views: 13107
Reputation: 66
I would recommed to use FormData to keep all the form data clean and iterable, once you have submited your form, data goes to the event submit and becomes easier to use.
Using the FormData API:
const formData = new FormData(event.target)
This way it becomes easier and cleaner to create an object to work with.
formData returns and FormData Object witch is iterable.
const body = {}
formData.forEach((value, property) => body[property] = value)
So now that you have built a new object you can update, delete, add, encrypt, or wharever you want it's properties and/or values.
Also there is not need to use a function to handle changes on inputs with the onChange
method since HTML does it for you natively, just use defaultValue=""
and required
atributes to keep your inputs filled in all of your fields.
Sometimes HTML is just enought for the work we want :)
Here is a little example of all said above.
import React from 'react';
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
//no needed.
}
}
onSubmitForm = e => {
e.preventDefault()
const formData = new FormData(e.target)
const body = {}
formData.forEach((value, property) => body[property] = value)
//here you can update, remove, add values/properties in the body object this is specially usefull if any custom process must be done to check, encrypt data or wherever you want.
console.table(body)
// Request goes here.
}
render() {
return (
<div className="App">
<form onSubmit={e => this.onSubmitForm(e)}>
<input name="name" type="text" defaultValue="" required />
<input name="lastname" type="text" defaultValue="" required />
<button type="submit">submit</button>
</form>
</div>
)
}
}
If you want to learn more about how to handle errors in React please have a look of Error Boundaries.
Upvotes: 1
Reputation:
It's OK! Here is working code. To initialaize the correct POST request, I use the if operator:
import React, { Component } from 'react';
import axios from 'axios';
class Misc extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
value: [],
loading: true,
error: false,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value })
}
componentDidMount() {
axios.get('http://localhost:5555/data')
.then(res => {
const data = res.data.data; // get the data array instead of object
this.setState({ data, loading: false });
})
.catch(err => { // log request error and prevent access to undefined state
this.setState({ loading: false, error: true });
console.error(err);
})
}
handleSubmit(e) {
e.preventDefault();
const data = {
phone: this.state.phone ? this.state.phone : this.state.data[0].phone,
email: this.state.email ? this.state.email : this.state.data[0].email,
title: this.state.title ? this.state.title : this.state.data[0].title,
longTitle: this.state.longTitle ? this.state.longTitle : this.state.data[0].longTitle,
introTitle: this.state.introTitle ? this.state.introTitle : this.state.data[0].introTitle,
introLongTitle: this.state.introLongTitle ? this.state.introLongTitle : this.state.data[0].introLongTitle,
videoLink: this.state.videoLink ? this.state.videoLink : this.state.data[0].videoLink,
introText: this.state.introText ? this.state.introText : this.state.data[0].introText
};
//console.log(this.state.phone);
axios.post('http://localhost:5555/data', { data })
.then(res => {
console.log(data);
console.log(this.state.data[0]);
})
}
render() {
if (this.state.loading) {
return (
<div>
<p> Loading... </p>
</div>
)
}
if (this.state.error || !this.state.data[0]) { // if request failed or data is empty don't try to access it either
return (
<div>
<p> An error occured </p>
</div>
)
}
return (
<form action="" onSubmit={this.handleSubmit}>
<h2 className="center" >Изменить данные</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn" /></div>
<h5>Phone:</h5>
<input type="text" name="phone" defaultValue={this.state.data[0].phone} onChange={e => this.onChange(e)} />
<h5>Email:</h5>
<input type="text" name="email" defaultValue={this.state.data[0].email} onChange={e => this.onChange(e)} />
<h5>Title:</h5>
<input type="text" name="title" defaultValue={this.state.data[0].title} onChange={e => this.onChange(e)} />
<h5>Description:</h5>
<input type="text" name="longTitle" defaultValue={this.state.data[0].longTitle} onChange={e => this.onChange(e)} />
<h2 className="center" >Intro:</h2>
<div className="center"><img src={require('../img/orn.png')} alt="" className="orn" /></div>
<h5>Title:</h5>
<input type="text" name="introTitle" defaultValue={this.state.data[0].introTitle} onChange={e => this.onChange(e)} />
<h5>Description:</h5>
<input type="text" name="introLongTitle" defaultValue={this.state.data[0].introLongTitle} onChange={e => this.onChange(e)} />
<h5>Link to video:</h5>
<input type="text" name="videoLink" defaultValue={this.state.data[0].videoLink} onChange={e => this.onChange(e)} />
<h5>Text:</h5>
<textarea name="introText" id="" cols="30" rows="10" defaultValue={this.state.data[0].introText} onChange={e => this.onChange(e)}></textarea>
<button type="submit" className="btn-large waves-effect waves-light xbutton">Save</button>
</form>
);
}
}
export default Misc;
Upvotes: 0
Reputation: 6280
By looking at this.setState({ value: e.target.value });
, every change on the inputs will set a new value to the only state.value
property overwriting previous value.
I think you should modify onChange
to
onChange(e) {
this.setState({[e.target.name]: e.target.value})
}
On submit, in your handleSubmit
you will have the values as:
const data = {
phone: this.state.phone,
email: this.state.email,
title: this.state.title,
// same for other inputs ..
};
This updates the property in the state having the same name as the corresponding input.
Upvotes: 1