I am trying to enable/disable a form button based on if there are any characters in both the of the text input fields, but for some reason the state lengths are rendering an error with my conditional, despite when I log the state it shows up.
Error:
const isEnabled = this.state.subject.length > 0 && this.state.emails.length > 0;
//Uncaught TypeError: Cannot read property 'length' of undefined
Full Code:
import React from 'react';
export default class EmailAnnotationForm extends React.Component {
constructor(props) {
super(props);
this.state = {
csrf: '',
subject: '',
emails: '',
comment: ''
}
this.handleInputChange = this.handleInputChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleClearForm = this.handleClearForm.bind(this);
this.input = React.createRef();
}
componentDidMount() {
console.log(this.state.subject.length) // renders correct value => 0
this.setState({subject: this.props.title });
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleClearForm() {
this.setState({
csrf: '',
subject: '',
emails: '',
comment: ''
})
}
handleFormSubmit(event) {
var emailSubject;
{
this.state.subject ? emailSubject = this.state.subject : emailSubject = this.props.title
}; //
const body = {
subject: emailSubject,
emails: this.state.emails,
comment: this.state.comment
};
event.preventDefault();
var route = `${API_ROOT}` + '/api/annotation/' + this.props.annotationId + '/share/email';
fetch(route,
{
method: 'POST',
body: JSON.stringify(body),
compress: false,
headers: {
'X-CSRF-Token': this.props.csrf,
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => {
return res.json();
})
.then(data => {
this.handleClearForm();
this.props.shareFlashMessage('success');
this.setState({'flash': 'success'});
this.props.closeShareModal(false);
})
.catch(err => {
console.log(err);
this.props.shareFlashMessage('error');
this.setState({'flash': 'error'});
});
}
render() {
var emailSubject;
{
this.state.subject
?
emailSubject = this.state.subject
:
emailSubject = this.props.title
};
console.log(this.state) // csrf: '', subject: undefined, emails: '', comment: ''
console.log(this.state.subject) // renders undefined
const isEnabled = this.state.subject.length > 0 && this.state.emails.length > 0; //Uncaught TypeError: Cannot read property 'length' of undefined
return (
<div className="annotation-footer__share-form-email">
<form action={"/api/annotation/" + this.props.annotationId + "/share/email"} method="post" onSubmit={this.handleFormSubmit} name="annotationEmailShare" id="share-email-form">
<div className="input-group annotation-footer__share-form-email-inputs">
<p><b>Subject:</b></p>
<input type="text" name="subject" className="form-control" defaultValue={this.props.title} onChange={this.handleInputChange}/><br />
</div>
<div className="input-group annotation-footer__share-form-email-inputs">
<p><b>Emails (Comma separate each email address):</b></p>
<input type="text" name="emails" className="form-control" onChange={this.handleInputChange}/><br />
</div>
<div className="input-group annotation-footer__share-form-email-inputs">
<p><b>Additional Comment (Optional):</b></p>
<textarea name="comment" rows="4" className="form-control" onChange={this.handleInputChange}></textarea><br />
</div>
<button type="submit" disabled={!isEnabled}>Send Email</button>
</form>
</div>
)
}
}
It seems this.props.title is undefined.
To solve the issue, put the check on this.props.title value, and update the state only if it has a valid value. Like this:
componentDidMount() {
if(this.props.title)
this.setState({ subject: this.props.title });
}
Suggestion:
Instead of updating subject with props value in didMount method, do it in constructor itself, Like this:
this.state = {
csrf: '',
subject: props.title || '',
emails: '',
comment: ''
}
Related
As seen in the title, I need a form validation in accordance below code. I have tried to validate many times with nodemailer method before and it was working validation. Now I changed my method due to some issues therefore I try another method but I cant not implement that. Can anyone help me, please?
Here is my new contact form and its function.
const Form = () => {
function sendEmail(e) {
e.preventDefault();
emailjs
.sendForm(
'servxxxxxxxxxu',
'tempxxxxxxxxxxxxa',
e.target,
'userxxxxxxxxxxxxxxxxxxxx'
)
.then((res) => {
console.log(res);
})
.catch((err) => console.log(err));
}
return (
<div className="Contact">
<div className="wrapper">
<h1>Contact Form</h1>
<form onSubmit={sendEmail}>
<input
className="input-field"
type="text"
name="name"
placeholder="Name"
/>
<input
className="input-field"
type="text"
name="user_email"
placeholder="E-Mail"
/>
<textarea name="message" rows="4" placeholder="Message" />
<input type="submit" value="Send" />
</form>
</div>
</div>
);
};
export default Form;
This is my old validation, may be helpful to you.
const initialState = {
name: '',
subject: '',
email: '',
message: '',
sent: false,
nameError: '',
subjectError: '',
emailError: '',
messageError: '',
};
export default class Validation extends React.Component {
state = initialState;
handleName = (e) => {
this.setState({
name: e.target.value,
});
};
handleSubject = (e) => {
this.setState({
subject: e.target.value,
});
};
handleEmail = (e) => {
this.setState({
email: e.target.value,
});
};
handleMessage = (e) => {
this.setState({
message: e.target.value,
});
};
validate = () => {
let nameError = '';
let subjectError = '';
let emailError = '';
let messageError = '';
if (!this.state.name) {
nameError = 'Name cannot be blank!';
}
if (!this.state.subject) {
subjectError = 'Subject cannot be blank!';
}
if (this.state.message.length < 5) {
messageError = 'Message cannot be less 5 character!';
}
if (!this.state.email) {
emailError = 'E-mail cannot be blank!';
} else if (typeof this.state.email !== 'undefined') {
var mailformat = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!mailformat.test(this.state.email)) {
emailError = 'Incorrect e-Mail!';
}
}
if (emailError || nameError || subjectError || messageError) {
this.setState({ emailError, nameError, subjectError, messageError });
return false;
}
return true;
};
handleSubmit = (e) => {
e.preventDefault();
const isValid = this.validate();
if (isValid) {
console.log(this.state);
this.sendingMail();
this.setState(initialState);
}
};
sendingMail = () => {
let data = {
name: this.state.name,
subject: this.state.subject,
email: this.state.email,
message: this.state.message,
};
axios
.post('http://127.0.0.1:3001/api/form', data)
.then((res) => {
this.setState(
{
sent: true,
},
this.resetForm()
);
})
.catch(() => {
console.log('message not sent');
});
};
resetForm = () => {
this.setState({
name: '',
subject: '',
email: '',
message: '',
});
setTimeout(() => {
this.setState({
sent: false,
});
}, 3000);
};
}
you can merge all handle methods into 1
state = initialState;
handleInput = (e , stateName) => {
this.setState({ [`${stateName}`]: e.target.value})
};
with this jsx
<input type="text" placeholder="name" onChange={(e)=>{this.handleInput('name' ,e)}}
validation
var States = [ this.state.name, this.state.subject , this.state.email , this.state.message]
States.forEach((stateKey)=>{
if (!stateKey) this.setState({Error: `${stateKey} could not be blank`})
})
if (this.state.message.length < 5)this.setState({Error:'Message cannot be less 5 character!})
if (this.state.Error) return false;
return true
I have the following code:
import React, { Component } from 'react'
import axios from 'axios'
import Navbar from '../Navbar'
import { Avatar, TextField, Button, Container, CircularProgress } from '#material-ui/core'
import Alert from '#material-ui/lab/Alert'
class PrivateProfile extends Component {
constructor(props) {
super(props);
this.state = {
user: null,
id: null,
image: null,
pp: null,
username: 'AnonymousUser',
showSuccess: false
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleFileChange = this.handleFileChange.bind(this)
}
componentDidMount() {
axios.get('http://127.0.0.1:8000/users/profile')
.then(res => {
this.setState({
user: res.data,
id: res.data.id,
username: res.data.username,
pp: res.data.pp
})
})
.catch(err => console.log(err))
}
handleSubmit(e) {
e.preventDefault()
const fd = new FormData()
fd.append('pp', this.state.image)
fd.append('username', this.state.user.username)
fd.append('email', this.state.user.email)
fd.append('bio', this.state.user.bio)
const d = {
pp : this.state.image,
username : this.state.user.username,
email : this.state.user.email,
bio : this.state.user.bio
}
console.log('d', d)
console.log('fd', fd)
axios.put(`http://127.0.0.1:8000/users/profile/update/${this.state.id}/`, fd, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(res => {
this.setState({
user: res.data,
id: res.data.id,
pp: res.data.pp,
image: null,
username: res.data.username,
showSuccess: true
})
})
.catch(err => console.log(err))
}
handleChange(e) {
this.setState({
user: {
[e.target.name]: e.target.value
}
})
}
handleFileChange(e) {
this.setState({image: e.target.files[0]})
}
render() {
let message
let alert
if (this.state.user !== null) {
if (!this.state.user.bio) {
message = <h4>Please update your profile below.</h4>
}
if (this.state.showSuccess) {
alert = <Alert action={<Button onClick={() => this.setState({showSuccess: false})}>Close</Button>} severity='success'>Profile Successfully Updated</Alert>
}
return (
<div>
<Navbar />
<Container style={{background: '#f7f4e9'}}>
<div style={{height: '60px'}}></div>
<h2>Your Profile</h2>
<Avatar src={this.state.user.pp} alt={this.state.user.username} />
{message}
{alert}
<h4>Your data:</h4>
<form onSubmit={this.handleSubmit}>
<p>Profile Pic</p>
<input type="file" onChange={this.handleFileChange}/>
<br></br>
<br></br>
<TextField label='Username' name="username" onChange={this.handleChange} type="text" value={this.state.user.username} />
<br></br>
<br></br>
<TextField label='Email' name="email" onChange={this.handleChange} type="email" value={this.state.user.email} />
<br></br>
<br></br>
<TextField label='Bio' name="bio" onChange={this.handleChange} type="text" value={this.state.user.bio} />
<br></br>
<br></br>
<br></br>
<Button type="submit" value="submit">Update</Button>
</form>
</Container>
</div>
)
} else {
return <CircularProgress />
}
}
}
export default PrivateProfile
I get the error saying: Warning: A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
Can someone help me fix it.
Since you're initializing state values with null and using it like value={this.state.user.username}, and update the state, you'll get such error:
Warning: A component is changing a controlled input of type text to be uncontrolled.
To control it's state, use it like:
value={this.state.user.username || ''}
As per my comment, you have issue here:
handleChange(e) {
this.setState({
user: {
[e.target.name]: e.target.value
}
})
}
The user state will always change on your any input changes, you will need like:
handleChange(e) {
this.setState({
user: {
...this.state.user,
[e.target.name]: e.target.value
}
})
}
I have a react app,
I am trying to perform login using redux and PHP.
I have a component, the component contains a form. the user enters the password and email to the form. after submitting the form the data enter an async-await function called handleSubmit.
That function has another function called onSubmitLogin in the await.
from the onSubmit this goes to the actiOn creator in ajax.js file.
the next step is the API PHP code, in there the PHP function checks if the user exists.
now from there to the reducer and back to function via mapStateToProps,
I want the states notActiveUserError and UserDoesNotExist to change depending on the props (this.props.auth) value I receive from the reducer using the checkUserValidation function.
The problem I have is that the props change but the state is not changing on the first run, every other time it works amazing but it never works on the first time after page loads.
Any help would be great.
this is the code I have:
handleSubmit is in LoginForm.js (full component is at the bottom of the question)
handleSubmit = async (event) => {
await this.onSubmitLogin(event);
this.checkUserValidation();
}
onSubmitLogin is in LoginForm.js (full component is at the bottom of the question)
onSubmitLogin(event){
event.preventDefault();
if(this.clientValidate()){
this.clientValidate();
}else{
let userData ={
email: this.state.email,
password: this.state.password
}
this.props.userLogin(userData);
}
}
the action creator
export const userLogin = (userData) => {
return (dispatch) => {
axios({
url: `${API_PATH}/users/Login.php`,
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
data: JSON.stringify(userData)
})
.then(function(response) {
dispatch({ type: USER_LOGIN, value: response.data });
})
.catch(function(error) {
console.log(error);
});
}
}
LoginForm component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Redirect, Link } from 'react-router-dom';
import {
Button,
Form,
FormGroup,
FormControl,
Col,
Alert,
Grid,
Row
} from 'react-bootstrap';
import { userLogedIn } from '../../actions';
import { userLogin } from '../../actions/ajax';
class LoginForm extends Component {
constructor() {
super();
this.state={
email: '',
username: '',
password: '',
auth: false,
usernameError: '',
passwordError: '',
EmptyUsernameError: '',
EmptyPasswordError: '',
notActiveUserError: '',
UserDoesNotExist: '',
userid: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
clientValidate = () => {
let isError = false;
if(this.state.email === ''){
this.setState({EmptyUsernameError: 'לא הזנתם דואר אלקטרוני'});
}
if(this.state.password === ''){
isError = true;
this.setState({EmptyPasswordError: 'לא הזנתם סיסמה'});
}
return isError;
}
checkUserValidation(){
if(this.props.auth === false && this.props.userid !== undefined){
console.log('this.props 1', this.props);
this.setState({notActiveUserError: 'חשבון לא מאומת'});
}
if(this.props.auth === false && this.props.userid === undefined){
console.log('this.props 2', this.props);
this.setState({UserDoesNotExist: 'משתשמ לא קיים'});
}
}
onSubmitLogin(event){
event.preventDefault();
if(this.clientValidate()){
this.clientValidate();
}else{
let userData ={
email: this.state.email,
password: this.state.password
}
this.props.userLogin(userData);
}
}
handleSubmit = async (event) => {
await this.onSubmitLogin(event);
this.checkUserValidation();
}
redirectUser = () => {
if(this.props.auth === true && this.props.userid != null){
const timestamp = new Date().getTime(); // current time
const exp = timestamp + (60 * 60 * 24 * 1000 * 7) // add one week
let auth = `auth=${this.props.auth};expires=${exp}`;
let userid = `userid=${this.props.userid};expires=${exp}`;
document.cookie = auth;
document.cookie = userid;
return <Redirect to='/records/biblist' />
}
}
onChange(event){
this.setState({
[event.target.name]: event.target.value,
auth: false,
usernameError: '',
EmptyPasswordError: '',
EmptyUsernameError: '',
notActiveUserError: '',
UserDoesNotExist: ''
})
}
isLoggedIn = () =>{
console.log(' this.props.auth ', this.props.auth);
}
render() {
this.isLoggedIn();
return (
<Form>
<FormGroup controlId="formHorizontalusername">
<Col xs={12} sm={5} style={TopMarginLoginBtn}>
<Row style={marginBottomZero}>
<FormControl ref="email" name="email" type="email" onChange={this.onChange} placeholder="דואר אלקטרוני" aria-label="דואר אלקטרוני"/>
</Row>
</Col>
<Col xs={12} sm={4} style={TopMarginLoginBtn}>
<Row style={marginBottomZero}>
<FormControl ref="password" name="password" type="password" onChange={this.onChange} placeholder="הקלד סיסמה" aria-label="סיסמה"/>
</Row>
</Col>
<Col xs={12} sm={3} style={TopMarginLoginBtn} >
<Button onClick={this.handleSubmit} type="submit" className="full-width-btn" id="loginSubmit">התחבר</Button>
{this.redirectUser()}
</Col>
<Col xs={12}>
<Link to="/passwordrecovery">שכחתי את הסיסמה</Link>
</Col>
</FormGroup>
{
this.state.EmptyUsernameError ?
<Alert bsStyle="danger"> {this.state.EmptyUsernameError} </Alert> :
''
}
{
this.state.EmptyPasswordError ?
<Alert bsStyle="danger"> {this.state.EmptyPasswordError} </Alert> :
''
}
{
this.state.usernameError ?
<Alert bsStyle="danger"> {this.state.usernameError} </Alert> :
''
}
{
//PROBLEM!! state updates before props
this.state.notActiveUserError ?
<Alert bsStyle="danger">{this.state.notActiveUserError}</Alert> :
''
}
{
//PROBLEM!! state updates before props
this.state.UserDoesNotExist ?
<Alert bsStyle="danger">{this.state.UserDoesNotExist} </Alert> :
''
}
<Row className="show-grid">
</Row>
</Form>
);
}
}
const bold={
fontWeight: 'bolder'
}
const mapDispatchToProps = dispatch => {
return {
userLogedIn: (params) => dispatch(userLogedIn(params))
};
};
const mapStateToProps = state => {
return {
userid: state.authReducer.userid,
auth: state.authReducer.auth,
email: state.authReducer.email
}
}
export default connect(mapStateToProps, {userLogedIn, userLogin})(LoginForm);
If you want to use async-await in your component then you have to move your API call to your component because when you call the action from component it doesn't return data back to your component.
if you want to use redux then I suggest you should remove async-await from your component it won't work, instead use the redux state to store success or failed state and handle that change in your component from getDerivedStateFromProps
export const userLogin = (userData) => {
return (dispatch) => {
dispatch({ type: USER_LOGIN_BEGIN }); // reset error/login state
axios({
url: `${API_PATH}/users/Login.php`,
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
data: JSON.stringify(userData)
})
.then(function(response) {
dispatch({ type: USER_LOGIN, value: response.data });
})
.catch(function(error) {
dispatch({ type: USER_LOGIN_FAILED, value: error });
});
}
}
in your component
onSubmitLogin(event){
event.preventDefault();
if(!this.clientValidate()){
let userData ={
email: this.state.email,
password: this.state.password
}
this.props.userLogin(userData);
}
}
handleSubmit = (event) => {
this.onSubmitLogin(event);
// this.checkUserValidation // move this logic to reducer and set error there according to response
}
static getDerivedStateFromProps(nextProps, prevState) {
// handle success/error according to your need and return update state
}
I created this.state.data object. Now I need to put this.state.email and this.state.password into this.state.data.email2 and this.state.data.password2
I want to create local storage. To do that I need an object where I could store data. this.state.email and this.state.password are inputs.
class Register extends Component {
constructor(props){
super(props);
this.state = {
email: '',
password: '',
data: {
email2: '',
password2: '',
},
}
// This binding is necessary to make `this` work in the callback
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleEmailChange = (event) => {
this.setState({email: event.target.value});
}
handlePasswordChange = (event) => {
this.setState({password: event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
console.log(this.state.email);
console.log(this.state.password);
/*
Take values from input, ant put it into this state data array
*/
// Reset form;
this.setState({
email: '',
password: '',
})
}
When I activate handleSubmit method I expect to take this.state.email, and this.state.password. And put it into object this.state.data
Hope you need to pass this.state.email and this.state.password to this.state.data
You can do that in handleEmailChange and handlePasswordChange itself, and your using arrow functions, so don't need to bind this in constructor.
Check a code below:
class App extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
data: {
email2: '',
password2: '',
},
}
}
handleEmailChange = (event) => {
this.setState({
email: event.target.value,
data: {
...this.state.data,
email2: event.target.value,
}
});
}
handlePasswordChange = (event) => {
this.setState({
password: event.target.value,
data: {
...this.state.data,
password2: event.target.value,
}
});
}
handleSubmit = (event) => {
event.preventDefault();
console.log(this.state.email);
console.log(this.state.password);
console.log('object data');
console.log(this.state.data);
/*
Take values from input, ant put it into this state data array
*/
// Reset form;
this.setState({
email: '',
password: '',
}, () => console.log(this.state))
}
render() {
return (
<div>
<input type="text" onChange={this.handleEmailChange} value={this.state.email} />
<br/><br/>
<input type="text" onChange={this.handlePasswordChange} value={this.state.password} />
<br/><br/>
<button type="button" onClick={this.handleSubmit}>Submit</button>
</div>
);
}
}
Working demo
and don't need to write separate events for similar functionalities, Check the demo once, you can do it like below:
<input type="text" data-field = "email" onChange={this.handleChange} value={this.state.email} />
<input type="text" data-field = "password" onChange={this.handleChange} value={this.state.password} />
and in handleChange
handleChange = (event) => {
this.setState({
[event.target.getAttribute('data-field')]: event.target.value,
data: {
...this.state.data,
[`${event.target.getAttribute('data-field')}2`]: event.target.value,
}
});
}
Hope this helps.
Like this (assuming your setup supports spread operator ... )
handleEmailChange = event => {
this.setState({ email: event.target.value });
this.setState(prevState => ({ data: { ...prevState.data, email2: event.target.value } }));
};
handlePasswordChange = event => {
this.setState({ password: event.target.value });
this.setState(prevState => ({ data: { ...prevState.data, password2: event.target.value } }));
};
You can do like this
handleSubmit = (event) => {
event.preventDefault();
console.log(this.state.email);
console.log(this.state.password);
const {data} = this.state;
data.email2 = this.state.email;
data.password2 = this.state.password;
this.setState({ data });
// Reset form;
this.setState({
email: '',
password: '',
})
}
or without mutating the state (good practice)
this.setState(prevState => ({
data: {
...prevState.data,
[data.email2]: this.state.email
[data.password2]: this.state.password
},
}));
I'm fighting with my app since long time and slowly there is progress however I have still problem with one thing
I want to pass function thought props from Form Component to List component, after that I wish to check if button add was clicked if yes then I wish to launch function getMovie() inside List component and send another request to json database. with edit and remove it works as there are in same component, with adding button it is a bit more tricky.
the problem is that if I write just
else if (this.props.addClick) {
this.getMovie();
}
it's keep sending requests to database over and over
below is my code
Form Component
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
type: '',
description: '',
id: '',
movies: [],
errors: "",
}
}
handleSubmit = e => {
e.preventDefault()
const url = `http://localhost:3000/movies/`;
if (this.state.name != "" && this.state.type != "" && this.state.description != "") {
axios
.post(url, {
name: this.state.name,
type: this.state.type,
description: this.state.description,
id: this.state.id,
})
.then(res => {
this.setState({
movies: [this.state.name, this.state.type, this.state.description, this.state.id]
})
})
.then(this.setState({
isButtonRemoveClicked: true
}))
}
else {
this.setState({
errors:"Please, Fill all forms above"
})
}
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Movie" onChange={this.handleChangeOne}/>
<input type="text" placeholder="Type of movie" onChange={this.handleChangeTwo}/>
<textarea placeholder="Description of the movie"
onChange={this.handleChangeThree}></textarea>
<input id="addMovie" type="submit" value="Add movie" ></input>
<p>{this.state.errors}</p>
</form>
<List removeClick={this.handleRemove} editClick={this.editMovie} addClick={this.handleSubmit}/>
</div>
)
List Component
class List extends React.Component {
constructor(props) {
super(props)
this.state = {
movies: [],
isButtonRemoveClicked: false,
}
}
componentDidMount() {
this.getMovie()
}
componentDidUpdate() {
if (this.state.isButtonRemoveClicked === true) {
this.getMovie();
this.timer = setTimeout(() => {
this.setState({
isButtonRemoveClicked: false
})
}, 10)
}
else if (this.props.addClick === true) {
this.getMovie();
}
}
componentWillUnmount() {
clearTimeout(this.timer)
}
getMovie = () => {
const url = `http://localhost:3000/movies`;
axios
.get(url)
.then(res => {
const movies = res.data;
this.setState({
movies: movies,
})
})
.catch((err) => {
console.log(err);
})
}
There is nothing magical ;)
You're start loading data from componentDidUpdate() ... data loads, componentDidUpdate is fired again, again...
Don't handle events this way.
If your main objective is to call function in child component from parent component, then you can use refs.
Example in your code :-
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
type: '',
description: '',
id: '',
movies: [],
errors: "",
}
}
handleSubmit = e => {
e.preventDefault()
const url = `http://localhost:3000/movies/`;
if (this.state.name != "" && this.state.type != "" && this.state.description != "") {
axios
.post(url, {
name: this.state.name,
type: this.state.type,
description: this.state.description,
id: this.state.id,
})
.then(res => {
this.setState({
movies: [this.state.name, this.state.type, this.state.description, this.state.id]
})
})
.then(
this.list.getMovie(); // call child function here
this.setState({
isButtonRemoveClicked: true
}))
}
else {
this.setState({
errors:"Please, Fill all forms above"
})
}
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Movie" onChange={this.handleChangeOne}/>
<input type="text" placeholder="Type of movie" onChange={this.handleChangeTwo}/>
<textarea placeholder="Description of the movie"
onChange={this.handleChangeThree}></textarea>
<input id="addMovie" type="submit" value="Add movie" ></input>
<p>{this.state.errors}</p>
</form>
<List
ref={list => this.list=list } // Create ref here
removeClick={this.handleRemove}
editClick={this.editMovie}
addClick={this.handleSubmit}/>
</div>
)
And in list component no need to use componentDidUpdate getMovie() call.
class List extends React.Component {
constructor(props) {
super(props)
this.state = {
movies: [],
isButtonRemoveClicked: false,
}
}
componentDidMount() {
this.getMovie()
}
getMovie = () => {
const url = `http://localhost:3000/movies`;
axios
.get(url)
.then(res => {
const movies = res.data;
this.setState({
movies: movies,
})
})
.catch((err) => {
console.log(err);
})
}
I think you are handling events in an overcomplicated manner. Why don't you lift props from inside the List component and just trigger the desired behaviour in the Form?. For example:
class List extends React.Component {
handleAddClick() {
this.props.onAddClick()
}
handleEditClick() {
this.props.onEditClick()
}
handleRemoveClick() {
this.props.onRemoveClick()
}
render() {
return (
<div>
<button onClick={() => this.handleAddClick()}>Add</button>
<button onClick={() => this.handleEditClick()}> Edit</button>
<button onClick={() => this.handleRemoveClick()} > Remove</button>
</div>
})
}
and
class Form extends React.Component {
getMovie() {
// Make AXIOS request
}
handleAdd() {
this.getMovie();
}
handleRemove() {
// REMOVE CODE
}
handleEdit() {
// EDIT CODE
}
render() {
<form>
{/* Form elements */}
<List
onAddClick={() => this.handleAdd()}
onRemoveClick={() => this.handleRemove()}
onEditClick={() => this.handleEdit()}
/>
</form>
}
}