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;
Related
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}
I want to display data from the autocomplete in the input as indicated below:
Autocomplete function
When i'm trying to do this i get an error:
×
TypeError: Cannot read property 'setState' of undefined
onSelect
94 | onSelect={ value => this.setState({ value }) }
I'm stuck on this and i'm probably doing it wrong. Hopefully someone can help me because i've tried everything i know and just cant see the problem. So please help me :)
privateMovie.js
import React, { useState, useEffect, setState } from "react";
import Layout from "../core/Layout";
import axios from "axios";
import { isAuth, getCookie, signout, updateUser } from "../auth/helpers";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";
import Autocomplete from "react-autocomplete";
import { MoviesData, renderMovieTitle } from "./movie-data";
const Private = ({ history }) => {
const [values, setValues ] = useState({
value: "",
suggestions: [],
movie: "",
buttonText: "Submit"
});
const token = getCookie("token");
useEffect(() => {
loadProfile();
}, []);
const loadProfile = () => {
axios({
method: "get",
url: `${process.env.REACT_APP_API}/user/${isAuth()._id}`,
headers: {
Authorization: `Bearer ${token}`
}
})
.then(response => {
console.log("PRIVATE PROFILE UPDATE", response);
const { movie } = response.data;
setValues({ ...values, movie });
})
.catch(error => {
console.log("PRIVATE PROFILE UPDATE ERROR", error.response.data.error);
if (error.response.status === 401) {
signout(() => {
history.push("/");
});
}
});
};
const { movie, buttonText } = values;
const handleChange = value => event => {
// console.log(event.target.value);
setValues({ ...values, [value]: event.target.value });
};
const clickSubmit = event => {
event.preventDefault();
setValues({ ...values, buttonText: "Submitting" });
axios({
method: "POST",
url: `${process.env.REACT_APP_API}/movie/create`,
headers: {
Authorization: `Bearer ${token}`
},
data: { movie }
})
.then(response => {
console.log("PRIVATE PROFILE UPDATE SUCCESS", response);
updateUser(response, () => {
setValues({ ...values, buttonText: "Submitted" });
toast.success("Profile updated successfully");
});
})
.catch(error => {
console.log("PRIVATE PROFILE UPDATE ERROR", error.response.data.error);
setValues({ ...values, buttonText: "Submit" });
toast.error(error.response.data.error);
});
};
const updateForm = () => (
<form>
<div className="form-group">
<label className="text-muted">AUTOCOMPLETE</label>
<Autocomplete
type="text"
getItemValue={item => item.title}
items={MoviesData()}
shouldItemRender={renderMovieTitle}
renderItem={(item, isHighlighted) => (
<div style={{ background: isHighlighted ? "lightgray" : "white" }}>
{item.title}
</div>
)}
onChange={(event, value) => this.setState({ value }) }
onSelect={ value => this.setState({ value }) }
/>
<input
onChange={handleChange("movie")}
value={movie}
type="text"
className="form-control"
/>
</div>
<div>
<button className="btn btn-primary" onClick={clickSubmit}>
{buttonText}
</button>
</div>
</form>
);
return (
<Layout>
<div className="col-md-6 offset-md-3">
<ToastContainer />
<h1 className="pt-5 text-center"></h1>
<p className="lead text-center"></p>
{updateForm()}
</div>
</Layout>
);
};
export default Private;
I think you mixed it up.
Instead you call this.setState()
call your destructed function setValues()
wich you have declared in the beginning of your component function
const [values, setValues ] = useState({
value: "",
suggestions: [],
movie: "",
buttonText: "Submit"
});
And if you use "useState" and destruct it in "getValues" and "setValues" you can get rid of setState in your import.
See the docs:
https://reactjs.org/docs/hooks-state.html
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
}
});
I'm newish to react. I have a fetch call in my App Component that I assign to a state. I pass that state as a prop along with a function to make a post to a child component. In my child component you can post/delete to alter the props, currently don't have a push() to add the new contact/prop. Is there a way to alter the parent component's state after I change the childs props? is there a better way to do this?
I'm trying to get the post action to update the state on the App.
App code
class App extends Component {
constructor() {
super();
this.state= {
contacts:[],
addModalShow: false,
modalIsOpen: false
}
}
componentDidMount() {
var request = new Request('http://localhost:3000/', {
method: "GET",
});
fetch(request)
.then((res) => {
res.json()
.then((data) => {
this.setState({
contacts: data.rows
})
})
})
}
toggleModal() {
this.setState({
modalIsOpen: ! this.state.modalIsOpen
})
}
addContact(event) {
this.toggleModal()
event.preventDefault();
let contactData = {
first: this.refs.first.value,
last: this.refs.last.value,
phone: this.refs.phone.value,
email: this.refs.email.value,
};
var request = new Request('http://localhost:3000/add', {
method: "POST",
headers: new Headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify(contactData)
});
console.log(this.state)
fetch(request)
.then((res) => {
res.json()
.then((data) => {
})
})
.catch((err) => {
console.log(err)
})
}
render() {
return (
<Container>
{console.log(this.state)}
<AddContact addContact={this.addContact} contacts={this.state.contacts} />
<ContactList contacts={this.state.contacts} />
<Contacts contacts={this.state.contacts}/>
</Container>
);
}
}
export default App;
Child component
class AddContact extends Component {
constructor(props) {
super(props);
this.state = {
contacts: [],
modalIsOpen: false,
}
}
toggleModal() {
this.setState({
modalIsOpen: ! this.state.modalIsOpen
})
}
render() {
return(
<Container>
<div className='header'>
<h1>
My Contacts
<button className='addContactButton' onClick={this.toggleModal.bind(this)}>+</button>
</h1>
<hr />
</div>
<Modal isOpen={this.state.modalIsOpen}>
<form ref='addContact' >
<div className='addContactHeader'>
<button className='saveButton' onClick={this.props.addContact.bind(this)}>Save</button>
<button className='cancelButton' onClick={this.toggleModal.bind(this)}>Cancel</button>
</div>
<div id="circle">
Add Photo
</div>
<div className="inputFields">
<div className='nameInputs'>
<input type='text' ref='first' placeholder='first name' />
<input type='text' ref='last' placeholder='last name' />
</div>
<div className='extraInputs' >
<input type='text' ref='phone' placeholder='phone' />
<input type='text' ref='email' placeholder='email' />
</div>
</div>
</form>
</Modal>
</Container>
)
}
}
Thanks for your time
You could use a callback function in order to update the state on the parent component (Another approach would be to use Redux updating the value in the Store, that way both components could have access to the value), here's how you could use the callback (With a little bit of ES6 refactor):
App:
class App extends Component {
state= {
contacts:[],
addModalShow: false,
modalIsOpen: false
}
componentDidMount() {
let request = new Request('http://localhost:3000/', {
method: "GET",
});
fetch(request)
.then((res) => {
res.json()
.then((data) => { this.setState({ contacts: data.rows }) })
})
}
toggleModal = () => {
this.setState({ modalIsOpen: ! this.state.modalIsOpen })
};
addContact = event => {
this.toggleModal()
event.preventDefault();
let contactData = {
first: this.refs.first.value,
last: this.refs.last.value,
phone: this.refs.phone.value,
email: this.refs.email.value,
};
let request = new Request('http://localhost:3000/add', {
method: "POST",
headers: new Headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify(contactData)
});
fetch(request)
.then((res) => {
res.json()
.then((data) => {
})
})
.catch((err) => {
console.log(err)
})
};
changeContacts = (newData) => {
this.setState({ contacts: newData });
};
render() {
const { contacts } = this.state;
return (
<Container>
<AddContact
addContact={this.addContact}
contacts={contacts}
onChildAction={this.changeContacts}
/>
<ContactList contacts={contacts} />
<Contacts contacts={contacts}/>
</Container>
);
}
}
export default App;
AddContacts:
class AddContact extends Component {
state = {
contacts: [],
modalIsOpen: false,
}
toggleModal = () => {
this.setState({ modalIsOpen: ! this.state.modalIsOpen })
};
// Here is where you'll send the info for the change of the prop
changeProp = e => {
const { onChildAction } = this.props;
onChildAction('Your new state/prop value here')
addContact(e);
};
render() {
const { changeProp } = this.props;
const { modalIsOpen } = this.state;
return(
<Container>
<div className='header'>
<h1>My Contacts
<button className='addContactButton' onClick={this.toggleModal}>+</button>
</h1>
<hr />
</div>
<Modal isOpen={modalIsOpen}>
<form ref='addContact' >
<div className='addContactHeader'>
<button className='saveButton' onClick={changeProp}>Save</button>
<button className='cancelButton' onClick={this.toggleModal}>Cancel</button>
</div>
<div id="circle">Add Photo</div>
<div className="inputFields">
<div className='nameInputs'>
<input type='text' ref='first' placeholder='first name' />
<input type='text' ref='last' placeholder='last name' />
</div>
<div className='extraInputs' >
<input type='text' ref='phone' placeholder='phone' />
<input type='text' ref='email' placeholder='email' />
</div>
</div>
</form>
</Modal>
</Container>
)
}
}
The last thing you need to do is decide where you want the change of the state/prop to be fire. Hope this helps.
to handle the parent from child you need to bind this to the child
Parent Component
class Component extends React.Component {
constructor(props) {
super(props)
this.state= {
contacts:[],
addModalShow: false,
modalIsOpen: false
}
this.addContact = this.addContact.bind(this);
}
render() {
...
return <AddContact addContact = {this.addContact} />
}
addContact(event) {
...
alert('one contact added');
...}
}
inside AddContact Component :
you can call this.props.addContact() to excute the parent function
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
});
};