function works only in debbuger React - javascript

I'm pretty new with React so I need your help.
I have a component with multiple inputs, so i have to validate them.
Submit button is disabled by default, and only if the inputs are not blank, I make it able to Submit.
If I delete the value inside the input, button should go back to disabled.
My problem is, this function(validate function) works only in debugger when I go step by step.
Can someone help me?
Here are segments of my code that I find useful to understand my problem.
this.state = {
profile: newProfile,
disable: true,
};
let newProfile= {
firstName: "",
lastName: "",
nickname: "",
email: ""
};
validate = () => {
console.log(this.state)
debugger;
if (!this.state.profile.name || !this.state.profile.email) {
return false;
} else {
console.log("Profile name and email NOT BLANK")
console.log(this.state)
return true;
}
};
profileChange= ((target, value) => {
this.setState(prevState => {
let profile= this.state.profile;
profile[target] = value;
return {
profile: profile,
}
})
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({disable: false});
}
else{
console.log("invalid inputs");
this.setState({disable: true});
}
});

setState is an asynchronous function (Why?) and at the point where you are calling this.validate, this.state.profile hasn't been set yet.
But when you are walk through the execution step by step, the state is being updated to the value you want and hence it is working for you.
The solution here is to use the callback function that setState provides to execute validate only after the state is set.
profileChange = (target, value) => {
this.setState(prevState => {
return {
profile: {
...prevState.profile,
[target]: value,
}
};
}, () => {
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({ disable: false });
} else {
console.log("invalid inputs");
this.setState({ disable: true });
}
});
};
Also note that I have used prevState inside the setState instead of this.state, so that the profile state is not actually mutated.

The setState function is asynchronous, which means that wile the state is being updated, other functions could be fired.
What I think is happening in your case, is that the state is being updated, but before that happened, this.validate() is already called.
To fix this, you have to add the code you would like to fire after you updated that state, as callback:
this.setState(prevState => {
let profile= this.state.profile;
profile[target] = value;
return {
profile: profile,
}
}, () => {
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({disable: false});
} else {
console.log("invalid inputs");
this.setState({disable: true});
}
});

you can use "disabled" params on input here's an example
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" disabled={!this.state.value}/>
</form>
);
}
}
and here's a codepen i made for you to test faster ragnar

Related

onClick does not work after removing "disabled" from (React) button

const MIN_LENGTH = 6;
class Login extends React.Component {
constructor() {
super();
this.state = {
email: '',
password: '',
emailValid: false,
passwordValid: false,
// redirect: false,
};
this.handleEmail = this.handleEmail.bind(this);
this.handlePassword = this.handlePassword.bind(this);
this.handleClick = this.handleClick.bind(this);
}
componentDidUpdate() {
this.handleButton();
}
handleEmail(ev) {
const email = ev.target.value;
const re = /^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
const isValid = re.test(email);
if (isValid) {
this.setState({ emailValid: true, email });
} else {
this.setState({ emailValid: false, email: '' });
}
}
handlePassword(ev) {
const size = ev.target.value.length;
if (size >= MIN_LENGTH) {
this.setState({ passwordValid: true, password: ev.target.value });
} else {
this.setState({ passwordValid: false, password: '' });
}
}
handleButton() {
const { emailValid, passwordValid } = this.state;
const btn = document.getElementById('btnLogin');
if (emailValid && passwordValid) {
btn.disabled = false;
} else {
btn.disabled = true;
}
}
handleClick(ev) {
console.log(ev);
ev.preventDefault();
console.log('click!');
}
render() {
const { email, password, emailValid, passwordValid } = this.state;
if (emailValid && passwordValid) {
console.log(`Email:${email}\nSenha:${password}`);
}
return (
<section>
<label htmlFor="email-input">
Email
<input type="email" data-testid="email-input" onChange={ this.handleEmail } />
</label>
<br />
<label htmlFor="password-input">
Password
<input
type="password"
data-testid="password-input"
onChange={ this.handlePassword }
/>
</label>
<br />
<button
type="button"
id="btnLogin"
onClick={ this.handleClick }
disabled
>
Login
</button>
</section>
);
}
}
export default Login;
The code above is a React component that will login my application. It is important to say that the validations are working perfectly, the button is only enabled when everything goes well, however when clicking the button nothing happens.The function "handleEmail" has the role of verifying via regex the validity of the email, and the email is only changed in the component state when it is valid. The "handlePassword" function only checks if the password entered is greater than or equal to 6. I'm storing this information in the component's state but the idea is to save this data in a global state in the future using "Redux". My only problem for now is the bug with the button's onClick.
You should not access the dom elements from the React directly, because react controls each component using js and does not see that you have changed the property in the html. You shouldn't do things like this one:
handleButton() {
const { emailValid, passwordValid } = this.state;
const btn = document.getElementById('btnLogin');
if (emailValid && passwordValid) {
btn.disabled = false;
} else {
btn.disabled = true;
}
}
You should add the property to the state, that stores the status of the button. Aka, add disabled boolean property and change it in the handler.
Your button gonna look like:
<button ... disabled={this.state.buttonDisabled} />
Your handleButton gonna look like this:
handleButton() {
const { emailValid, passwordValid } = this.state;
this.setState({
buttonDisabled: !(emailValid && passwordValid)
});
}

Cannot manage the fields not filled on Reactjs

I’m making a dynamic form in reactjs. But I have an annoying problem. I would only like to send the fields of the form that are filled out.
To do this, I use this piece of code that allows me to download all datas that are not filled.
Globally, my code for the sending part is
async handleSubmit(event) {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 2000);
event.preventDefault();
const {
name_contact='', name_contact1='', endfr='', endfr_1='',
} = this.state;
Object.keys(this.state).forEach(key => {
if (!this.state[key]) delete this.state[key];
})
await axios.post(
' MY_endpoint API',
{
name: `${name_contact},${name_contact_1} `,end: `${endfr},${endfr_1});
}
On this piece of code, I delete datas from this.state that are null to remove variables with nothing in them.
But my problem is that I don’t know how to handle the fact that a field is empty in axios.get.
I would like to be able to remove from get.axios the variables already deleted by
Object.keys(this.state).forEach(key => {
if (!this.state[key]) delete this.state[key];
})
For example if I do not fill in the name_contact_1 field, I would like axios.get to be
await axios.post(
' MY_endpoint API',
{
name: `${name_contact}`,end: `${endfr},${endfr_1});
}
or if i'm not puting nothing in endfr
await axios.post(
' MY_endpoint API',
{
name: `${name_contact},${name_contact_1} `,end: `${endfr_1});
}
So my question is : Does anyone have any idea how I can handle this? (It may not be possible with my code structure)
I’m not sure if I’m being clear enough, but if I’m not, tell me and I’ll make sure I change it.
PS: my full code
export default class FormPage extends Component {
constructor(props) {
super(props);
this.state = initialState;
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
showMessage = (bool) => {
setTimeout(() => {
this.setState({
showMessage: true
});
}, 2000);
if (this.state.Showing) return;
this.setState({ Show: true, Showing: true });
setTimeout(() => {
this.setState({ Show: false, Showing: false });
}, 2000);
}
showMessageFalse = (bool) => {
this.setState({
showMessage: false
});
this.setState(initialState);
}
handleChange(event) {
const InputValue = event.target.value;
const stateField = event.target.name;
this.setState({
[stateField]: InputValue,
});
console.log(this.state);
}
async handleSubmit(event) {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 2000);
event.preventDefault();
const {
name_contact='',
} = this.state;
Object.keys(this.state).forEach(key => {
if (!this.state[key]) delete this.state[key];
})
await axios.post(
' MY_endpoint API',
{
name: `${name_contact}`);
}
render() {
const { loading } = this.state;
return (
<div>
<ExpansionPanel title="Contacts" expandedTitle="Contacts" titleIcon="done_all" ><div>
<Container>
<div id="normal"><label id='title2'>Detail du contact</label></div><br/>
<Row align="center">
<Col id= "color" sm={3}> <label> Name: </label></Col> <Col id= "color" sm={3}><Input placeholder="Nom complet" type="string" name="name_contact" value={this.state.name_contact} onChange={this.handleChange}/><br /> </Col>
</Row>
</Container>
</div>
</ExpansionPanel>
<form onSubmit={this.handleSubmit}>
<br /><br /><div id="deb"><Button type="submit" value="Show" onClick={this.showMessageFalse.bind(null, true)} > Update </Button></div>
</form>
</div>
);
}
}
First of all, you need to understand that when you assign the value const {name_contact=''} = this.state; And then you later do delete this.state['name_contact'], like you've done above, you haven't actually changed the value of the name_contact variable. You've changed the value inside the state, but not the value itself.
Secondly, javascript cannot magically create your name string like that for you, with a comma only if there are 2 values. You'll have to write a function to do that.
Something like var name = [name_contact, name_contact1].filter(v =>v).join(','); The .filter function gets rid of the empty values from the array, and the .join turns it into a string with commas between values
So you'll end up with something more like this:
async handleSubmit(event) {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 2000);
event.preventDefault();
const {
name_contact='', name_contact1='', endfr='', endfr_1='',
} = this.state;
const name = [name_contact, name_contact1].filter(v =>v).join(',');
const end = [name_contact, name_contact1].filter(v =>v).join(',');
await axios.post(
' MY_endpoint API',
{
name, end
})
}

How can I check the value of this.state with an if statement and not console.log()?

I want to check the values of this.state.cityCodeval and this.state.idVal using an if statement inside the displayName() method so it can display what's inside the return() if the values inputted by the user are correct.
In my Webstorm IDE, I get a warning that says:
Binary operation argument type string is not compatible with type string
Which makes me believe I'm checking for their values the wrong way.
I know I could just do console.log(this.state.cityCodeval); or console.log(this.state.idVal);, but I need to check for what the user input is.
Here's my code
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actionType from '../../store/actions/actions';
class SearchArticle extends Component {
constructor(props) {
super(props);
this.state = {
flag: false,
idVal: '',
cityCodeval: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleArticleId = this.handleArticleId.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
console.log("IDValue --> " + this.state.idVal);
this.props.articleIdValueRedux(this.state.idVal);
this.setState({flag: true});
}
handleChange = event => {
this.setState({value: event.target.value});
this.props.cityCodeReducerRedux(event.target.value);
}
handleArticleId = event => {
event.preventDefault();
this.setState({idVal: event.target.value});
}
displayName = () => {
if(this.state.cityCodeval === 'nyc' && this.state.idVal === '1') {
return (
<div>
<p>author name: {this.state.authorNameValue}</p>
<p>article text: {this.state.storyTextValue}</p>
</div>
);
}
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.state.cityCodeValue} type="text" placeholder="city code"/>
<input onChange={this.handleArticleId} value={this.state.idVal} placeholder="article id"/>
<button type="submit" value="Search">Submit</button>
{this.state.flag ? this.displayName() : null}
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
cityCodeValue: state.cityCodeValue.cityCodeValue,
authorNameValue: state.authorNameValue.authorNameValue,
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
cityCodeReducerRedux: (value) => dispatch({type: actionType.CITY_CODE_VALUE, value}),
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchArticle);
You should still return null; as a safe clause just in case your condition doesn't match.
displayName = () => {
if(this.state.cityCodeval === 'nyc' && this.state.idVal === '1') {
console.log(this.state.cityCodeval); // console it here
console.log(this.state.idVal); // console it here
return (
<div>
<p>author name: {this.state.authorNameValue}</p>
<p>article text: {this.state.storyTextValue}</p>
</div>
);
}
return null;
}
Also now, in your render method you can do this.
{this.state.flag && this.displayName()}
This means that if the flag variable is true, call displayName it then executes the function. If you first condition matches it will return that otherwise it will return null;
Previously in your current code if the flag variable was true and it executed the function displayName where the if condition didn't meet. This caused an error because it had nothing to return.

React Redux Data not being passed to props

I am new to React/Redux and I am stuck in a problem. My fetched data from API is not being passed to props. It's always an empty object.
I see that there might be some issues that I am not even aware of but I don't have a clue where to look for.
Please check my codes below:
RegisterPage.jsx
import { connect } from 'react-redux';
import { userActions } from '../_actions';
class RegisterPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
first_name: '',
last_name: '',
properties_id: '',
email: '',
phone_number: '',
password: ''
},
submitted: false,
checked: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.props.dispatch(userActions.getAll());
}
handleChange(event) {
const { name, value } = event.target;
const { user } = this.state;
this.setState({
user: {
...user,
[name]: value
},
checked: !this.state.checked
});
}
handleSubmit(event) {
event.preventDefault();
this.setState({ submitted: true });
const { user } = this.state;
const { dispatch } = this.props;
if(this.state.checked) {
if (user.first_name && user.last_name && user.properties_id &&
user.email && user.phone_number && user.password) {
dispatch(userActions.register(user));
}
} else {
alert("Please tick the checkbox to agree to Terms and Conditions");
}
}
render() {
const { registering, properties } = this.props;
const { user, submitted } = this.state;
return (......)
}
function mapStateToProps(state) {
const { registering } = state.registration;
const { properties } = state;
return {
properties,
registering
};
}
const connectedRegisterPage = connect(mapStateToProps)(RegisterPage);
export { connectedRegisterPage as RegisterPage };
users.reducers.js
export function users(state = {}, action) {
switch (action.type) {
case userConstants.GETALL_REQUEST:
return {
loading: true
};
case userConstants.GETALL_SUCCESS:
return {
items: action.properties
//action.users
};
case userConstants.GETALL_FAILURE:
return {
error: action.error
};
default:
return state
}
}
user.actions.js
export const userActions = {
login,
logout,
register,
getAll,
delete: _delete
};
function getAll() {
return dispatch => {
dispatch(request());
userService.getAll()
.then(
properties => dispatch(success(properties)),
error => dispatch(failure(error.toString()))
);
};
function request() { return { type: userConstants.GETALL_REQUEST } }
function success(properties) { return { type: userConstants.GETALL_SUCCESS, properties } }
function failure(error) { return { type: userConstants.GETALL_FAILURE, error } }
}
user.service.js
// Get All Properties
function getAll() {
const requestOptions = {
method: 'GET'
};
return fetch(`${config.apiUrl}/api/properties`, requestOptions).then(handleResponse).then(
properties => {
return properties;
}
);
}
Here's the screenshot of the console:
It is clear that properties array is not empty. But when I am going to use properties, it is empty. I don't know what's wrong. If anyone could help figure out what's wrong with my code or something that I missed, your help will be greatly appreciated. I just need to fix this so I could move forward. Thanks in advance!
I thinking that your state tree might not contain state.properties but instead state.items. Unless if you did something in combineReducers() that changes the shape of it again.
case userConstants.GETALL_SUCCESS:
return {
items: action.properties
//action.users
};
This part would probably cause action.properties to be stored in state.items instead of state.properties
I'd recommend using ReduxDevTools to make your life with state easier

setState(…): Can only update a mounted or mounting component

This is the code. No idea as to why there is a problem.
class TeacherForm extends Component {
constructor({ data }) {
super();
this.isUpdatingForm = !! data;
this.state = Object.assign({ ... });
this.handleSubmit = this.handleSubmit.bind(this);
this.removeTeacher = this.removeTeacher.bind(this);
}
handleChange(value, field) {
this.setState({ shouldUpdate: true, [field]: value });
}
handleSubmit(e) {
e.preventDefault();
const { name, subjects, parttime, timing } = this.state;
if (this.isUpdatingForm) {
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => this.setState({ shouldUpdate: false }));
}
return add.call();
}
removeTeacher() {
return remove.call(this.props.data._id);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
...
</form>
);
}
}
The error gets thrown at the handleSubmit method in the callback of update.call. This normally shows up when I call removeTeacher and a list updates and this component unmounts.
It sounds like the callback () => this.setState({ shouldUpdate: false }) is executed after that the component is unmounted. Is that possible? If so, one way to get around that is to replace this part by
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => { !this.unmounted && this.setState({ shouldUpdate: false }); });
and to add
componentWillUnmount() {
this.unmounted = true;
}

Categories