javascript - why can't I update my form fields with React - javascript

I'm new to React. I have used Create-react-app to create my app. Now I'm trying to connect it to my back-end REST APIs. I have done that successfully for simply getting and displaying data. Now I'm trying to enable updating data via a form. I'm following the Forms page on the React docs. I had an earlier problem which was due to using an object in my state, which I thought I solved via this answer with the snippet in the setState() inside handleChange(). But I think that may have something to do with my current problem, which is that the form input fields don't update when I try to type anything in them.
Here's my js page:
import React, { Component } from 'react';
import { Container } from 'reactstrap';
import AppNavbar from './AppNavbar';
class TradeConfig extends Component {
constructor(props) {
super(props);
this.state = {tradeConfig: {}, isLoading: true};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
console.log("handleChange : " + event);
const target = event.target;
const value = target.value;
const name = target.name;
this.setState(oldState => {
return {
foo: Object.assign({}, oldState.tradeConfig, {[name]: value})
}
});
}
handleSubmit(event) {
alert('A form was submitted: ' + this.state.tradeConfig);
event.preventDefault();
fetch(process.env.REACT_APP_API_URL+'/api/config', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(this.state.tradeConfig)
}).then(() => {
fetch(process.env.REACT_APP_API_URL+'/api/config')
.then(response => response.json())
.then(data => this.setState({tradeConfig: data, isLoading: false}));
});
}
componentDidMount() {
this.setState({isLoading: true});
fetch(process.env.REACT_APP_API_URL+'/api/config')
.then(response => response.json())
.then(data => this.setState({tradeConfig: data, isLoading: false}));
}
render() {
const {tradeConfig, isLoading} = this.state;
if (isLoading) {
return <p>Loading...</p>;
}
return (
<div>
<AppNavbar/>
<Container fluid>
<h3>Trade Config</h3>
<form onSubmit={this.handleSubmit}>
<label>Position Size: </label>
<input
name="positionSize"
type="number"
value={this.state.tradeConfig.positionSize}
onChange={this.handleChange} />
<br/>
<label>Actively Trading:
<input
name="activelyTrading"
type="boolean"
value={this.state.tradeConfig.activelyTrading}
onChange={this.handleChange} />
</label>
<br/>
<input type="submit" value="Submit" />
</form>
</Container>
</div>
);
}
}
export default TradeConfig;
I have confirmed via console.log that the handleChange is being called.

Try setting your state like this in handleChange.
this.setState({
tradeConfig:{
...this.state.tradeConfig,
[name]:value
}
});

Related

How can I pass an object ID from mapped array to backend?

I want to pass the ID of an object to the backend. The objects are mapped from the array and there should be a separate button for each one so that the ID of each individual object can be pass to the backend.
The communication between backend and frontend works. The only problem is that the ID is not sent to the backend when the submit button is clicked. If I would now work with an OnChange and enter the ID myself in the text field, then it would work without any problems.
Does somebody has any idea?
Here my code:
import React from 'react';
import {format} from "date-fns-tz";
import {Link} from "react-router-dom";
import MailQueueDataService from "../services/mail_queue.service";
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {
mailqueues_unsent: {},
loading: false
}
this.parentClassFunction = this.parentClassFunction.bind(this);
}
parentClassFunction = () => {
console.log("TEST");
event.preventDefault();
const url = "/api/v1/mail_queues/authorize_mail_queue";
const { id } = this.state;
const body = {
id,
};
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "POST",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
})
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(response => this.props.history.push(window.close()))
.catch(error => console.log(error.message));
}
render() {
return (
<div>
<Child
parentClassFunction={this.parentClassFunction}
/>
</div>
)
}
}
class Child extends React.Component{
constructor(props){
super(props);
this.state = {
mail_queues_unsent: [],
loading: false
}
}
onClickSubmitButton = () =>{
this.props.parentClassFunction()
};
retrieveMailQueues() {
MailQueueDataService.getAll().then(response => {
if (this._isMounted)
this.setState({
mail_queues_unsent: response.data.mailqueues_unsent,
loading: false}
)
}).catch(e => {
console.log(e)
})
}
componentDidMount() {
this._isMounted = true;
this.setState({loading: true})
this.retrieveMailQueues();
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
if (this.state.loading) {
return <div className="col text-center"> Lade Unautorisierte Mails... </div>;
} else {
const {mail_queues_unsent} = this.state;
const allMailsUnsent = mail_queues_unsent.map((mailqueues_unsent, index) => (
<div className="col">
<div key={index}>
<h4><b>Empfänger:</b>{mailqueues_unsent.company_name}</h4>
<b>Datum Versandfreigabe:</b>
{format(new Date(mailqueues_unsent.created_at), 'dd.MM.yyyy hh:mm')}
<p><b>Anzahl der Tests:</b> {mailqueues_unsent.trials_count}</p>
<b>Tests:</b>
<p>{mailqueues_unsent.trials.map(trial => <Link to={"/trials/" + trial.id}>
<p>{trial.certificate_number}</p></Link>)}</p>
<form onSubmit={this.parentClassFunction}>
<label htmlFor="id"></label>
<input
type="text"
name="id"
id="id"
value={mailqueues_unsent.id}
className="form-control"
onChange={this.onChange}
/>
<button onClick={this.onClickSubmitButton.bind(this)}>CLICK</button>
</form>
</div>
</div>
));
const noMailQueues = (
<div>
<h4>
Kein Unautorisierte Mails vorhanden.
</h4>
</div>
);
return (
<div>
{mail_queues_unsent.length > 0 ? allMailsUnsent : noMailQueues}
</div>
)
}
}
}
export default Parent;
Since you are calling a function that is passed as prop from the parent, inside the child component you should call it on submit like this (its not this but this.props):
onSubmit={this.props.parentClassFunction}

React - form - unable to update/edit value in input/textarea - writing disable

Here is the code:
import React from 'react';
class EditEntry extends React.Component {
constructor(props) {
super(props);
this.state = {
entry: '',
description: '',
item: [],
error: null
};
this.handleChangeEntry = this.handleChangeEntry.bind(this);
this.handleChangeDescription = this.handleChangeDescription.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
const { match: { params } } = this.props;
const { id } = params;
fetch(`/api/entries/${id}`, {
method: 'GET'
})
.then(response => response.json())
.then(
data => {
this.setState({
item: data
});
},
error => {
this.setState({
error
});
});
}
handleChangeEntry(e) {
this.setState({ entry: e.target.value });
}
handleChangeDescription(e) {
this.setState({ description: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
const { entry, description } = this.state;
const { match: { params } } = this.props;
const { id } = params;
fetch(`/api/entries/${id}`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(entry, description)
})
.then(response => {
if (response.ok) {
window.location.assign("/");
} else {
throw new Error('No response.');
}
},
error => {
this.setState({
error
});
});
}
render() {
const { item } = this.state;
const itemEditForm = item.map(i => {
return <div key={i.id}>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="entry">Entry</label>
<input
id="entry"
name="entry"
type="text"
className="form-control"
required
value={i.entry}
onChange={this.handleChangeEntry} />
</div>
<div className="form-group">
<label htmlFor="description">Description</label>
<textarea
id="description"
name="description"
type="text"
className="form-control"
required
rows="5"
value={i.description}
onChange={this.handleChangeDescription} />
</div>
<button type="submit" className="btn btn-secondary btn-sm mb-sm-2">Submit</button>
</form>
</div>
});
return (
<div>
{itemEditForm}
</div>
);
}
}
export default EditEntry;
I can't edit the data contained in:
<input id="entry" (...) />
<textarea id="description" (...) />
There is data, there is a cursor, but these fields are disabled for editing.
I fetch an array with one object from the database (componentDidMount()). I know there is another way to access the data contained in an object. But that way in this project doesn't work. Although in my another project it works. Strange.
You are setting the "entry" and "description" properties in the onChange handler methods. but using the i.entry and i.description in the value property of the input. Please try changing it and maybe if you have multiple items in the item array, you can try something like below to handle state.
Component:
<input
id="name"
name="name"
type="text"
className="form-control"
required
value={i.name}
onChange={(e) => this.handleChangeName(e, i.id)}
/>
JS:
handleChangeName(e, id) {
console.log(e.target.value);
const newState = {
item: this.state.item.map((x) =>
x.id != id ? x : { id: id, name: e.target.value }
)
};
this.setState(newState);
}
Also, please find a working example based on your code on the CodeSandbox Link
Thanks.

Am I misunderstanding when componentDidMount is triggered?

I am trying to render information received via an API call in my componentDidMount. This works as expected, however, I am under the assumptions that by the time I render my react component, the componentDidMount would have been called already. This seems to not be the case as when I try to access the data in my react element, it is undefined, and I can see that it logs to the console before the component is mounted. Am I doing this wrong?
import React, { Component } from 'react';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
class App2 extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = { projectData: [] };
this.state = { value: '' }
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(e) {
alert("A update was submitted:" + JSON.stringify({ update: this.state.value }));
fetch("/put", {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ update: this.state.value }),
}).then(res => res.json())
.then(response => alert('Success:', JSON.stringify(response)))
.catch(error => console.log('Error:', error));
}
componentDidMount() {
console.log('componentDidMount');
fetch("/api")
.then(res => res.json())
.then(
(result) => {
// console.log('Result: ', result);
this.setState({
projectData: result
});
},
(error) => {
console.log(error);
this.setState({
error
});
}
)
}
render() {
const { error, projectData } = this.state;
console.log(projectData);
if (error) {
return <div>Error: {error.message}</div>;
} else
return (
<div className="App">
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
crossorigin="anonymous" />
<h1>Project Data:</h1>
{/* <ul>
{projectData.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul> */}
<Form id="formName" onSubmit={this.handleSubmit}>
<Form.Group>
<Form.Label>Update an item:</Form.Label>
<Form.Control type="text" placeholder="Enter update" id="update" name="update" onChange={this.handleChange} />
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</div>
);
}
}
export default App2;

AXIOS PUT request returns a 200 code but doesn't update content?

So I have a a component that returns a 200 code but for some reason the content does not update at all after I click the submit button. My goal is to update the 4 divs inside the form after submitting the form. The course state contains properties that contain info about each course, those properties are _id, description, estimatedTime, materialsNeeded and title.
Can someone help?
class UpdateCourse extends Component {
constructor(props) {
super(props);
this.state = {
course: []
};
this.handleSubmit = this.handleSubmit.bind(this);
}
change = e => {
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit = event => {
const {
match: { params }
} = this.props;
event.preventDefault();
const updateCourse = {
title: this.state.course.title,
description: this.state.course.description,
estimatedTime: this.state.course.estimatedTime,
materialsNeeded: this.state.course.materialsNeeded
};
axios({
method: "put",
url: `http://localhost:5000/api/courses/${params.id}`,
auth: {
username: window.localStorage.getItem("Email"),
password: window.localStorage.getItem("Password")
},
data: updateCourse
})
.then(response => {
//if the response came back as 204 then alert the user that the course was successfully updated, if another code came back then redirect them to the error handler
if (response.status === 204) {
alert("The course has been successfully updated!");
this.props.history.push("/");
} else {
throw new Error();
}
})
.catch(err => {
//use a catch method to catch the errors and display them is the status code comes back as 400
console.log("CATCH =", err.response.data.errors);
this.setState({
//if there were errors, then set the errors state in react to the error messages that came from the REST API
errors: err.response.data.errors
});
});
};
componentDidMount() {
const {
match: { params }
} = this.props;
axios
.get(`http://localhost:5000/api/courses/${params.id}`)
.then(results => {
this.setState({
course: results.data
});
});
}
render() {
return (
<div>
<div>
<form onSubmit={this.handleSubmit}>
<div>
<input
id="title"
name="title"
type="text"
className="input-title course--title--input"
placeholder="Course title..."
defaultValue={this.state.course.title}
onChange={e => this.change(e)}
/>
</div>
<div>
<textarea
id="description"
name="description"
placeholder={this.state.course.description}
defaultValue={this.state.course.description}
onChange={e => this.change(e)}
/>{" "}
</div>
<div>
<input
id="estimatedTime"
name="estimatedTime"
type="text"
className="course--time--input"
placeholder="Hours"
defaultValue={this.state.course.estimatedTime}
onChange={e => this.change(e)}
/>
</div>
<div>
<textarea
id="materialsNeeded"
name="materialsNeeded"
placeholder={this.state.course.materialsNeeded}
defaultValue={this.state.course.materialsNeeded}
onChange={e => this.change(e)}
/>
</div>
</form>
</div>
</div>
);
}
}
Please update with this:
constructor(props) {
super(props);
this.state = {
course: {}
};
this.handleSubmit = this.handleSubmit.bind(this);
this.change = this.change.bind(this);
}
change = e => {
const obj = { [e.target.name]: e.target.value };
const course = Object.assign({}, this.state.course, obj);
this.setState({
course
});
};

How can I redirect to correct page when successfully submitting form information?

As of now, I'm successfully inserting information into the database (SQL, phpMyAdmin) via Home.js but the problem is that every time the user enters information & hits submit, it gets redirected to my demo.php file (not provided) instead of Next.js.
In other words, how can I make it so that upon the user information successfully entering the database and go to the next page? (Next.js)?
What am I doing wrong and how can I fix this?
Here's Home.js:
import React, { Component } from 'react';
import Next from '../Home/Next';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
show: false
};
this.getPHP = this.getPHP.bind(this);
this.goNext = this.goNext.bind(this);
}
getPHP() {
fetch(`http://localhost/demo_react/api/demo.php`, {
method: 'POST'
}).then(res => res.json())
.then(response => {
console.log('response');
console.log(response);
});
}
goNext() {
this.setState({show: true});
}
render() {
const next = this.state.show;
if(next) {
return <Next/>;
}
return (
<div>
<br/>
<form className="form-control" action="http://localhost/demo_react/api/demo.php" method={"POST"} encType="multipart/form-data">
<input type="text" name="username"/>
<input type="text" name="password"/>
<input type="submit" value="submit" onClick={this.getPHP & this.goNext} name={"submit"}/>
</form>
</div>
);
}
}
export default Home;
Here's Next.js:
import React, { Component } from 'react';
class Next extends Component {
render() {
return(
<h1>made it</h1>
);
}
}
export default Next;
You need to remove the action property from your form and call getPHP() when form is submitted. Also, it's better to have controlled inputs (state of component change when input change). See this for more info: Get form data in Reactjs
<form className="form-control" onSubmit={e => this.getPHP(e)}>
<input type="text" name="username" value={this.state.username} onChange={e => this.setState({ username: e.target.value })} />
<input type="text" name="password" value={this.state.password} onChange={e => this.setState({ password: e.target.value })} />
<input type="submit" value="submit" name="submit" />
</form>
You can access to form values directly in getPHP() method because inputs are now controlled:
constructor(props) {
super(props);
this.state = {
show: false,
username: '',
password: '',
};
}
getPHP(e) {
e.preventDefault();
const { username, password } = this.state;
const formData = new FormData();
formData.append('username', username);
formData.append('password', password );
fetch(`http://localhost/demo_react/api/demo.php`, {
method: 'POST',
headers: new Headers({ 'Content-Type': 'multipart/form-data' }),
body: formData,
})
.then(res => res.json())
.then(response => {
console.log('response');
console.log(response);
this.goNext();
});
}
At the end, you can goNext() when the fetch succeed.

Categories

Resources