React Props doesn't update the state - reactjs

What is wrong with the below code in react? I see that the props are shown up with a delay (based on console.log) but other than that I don't see what is the issue here.
state = {
data: {
firstName: "",
lastName: "",
email: "",
},
errors: {},
};
componentDidMount() {
const { firstName, lastName, email } = this.props.user;
var changeData = { ...this.state.data };
changeData.firstName = firstName || "";
changeData.lastName = lastName || "";
changeData.email = email || "";
if (firstName) {
console.log(changeData);
this.setState({ data: { changeData } });
}
}
I don't see the data being changed in the state....

changeData is already an object no need to wrap with {} change your code like below
this.setState({ data: changeData });

Related

React JS Unhandled Rejection (TypeError): item.persistVariablesLimit.trim is not a function

I'm getting the following error:
React JS Unhandled Rejection (TypeError): item.persistVariablesLimit.trim is not a function
when I try to set a default value on the persistVariablesLimit.
Here is an image of the stack trace and console log:
Here's the code that generates it:
if (typeof item.persistVariablesLimit !== 'undefined') {
item.persistVariablesLimit = item.persistVariablesLimit.trim()
if (!item.persistVariablesLimit.match(/^[0-9]+$/)) {
formIsValid = false
errors['persistVariablesLimit'] = 'Must be numeric'
}
}
There is no problem with this field if the data is entered manually, even if it is entered, then deleted.
Also, if I don't set a default value, and don't enter anything into the field, it is saved successfully as an empty string.
Here's where it sets the default value (when the "FA" template is selected):
handleSelectTemplateChange = (event, { value }) => {
let item = { ...this.state.item }
item.template = value
var str1 = '' + item.template
if (str1.startsWith('SMA')) {
item.family = 'Safety-Machine-Analytics'
}
if (str1.startsWith('FA')) {
item.family = 'Field Analytics'
item.product = 'Field Analytics'
if (!item.persistVariablesLimit) {
item.persistVariablesLimit = 50;
}
if (!item.dataSourceLimit) {
item.dataSourceLimit = 50;
}
}
else {
item.persistVariablesLimit = "";
item.dataSourceLimit = "";
}
this.setState({ item })
}
This is the UI code for the template:
<Form.Select
fluid
label='Template'
options={this.state.templateOptions || []}
placeholder='Template'
name='template'
value={item.template}
required={true}
onChange={this.handleSelectTemplateChange}
/>
And for the persistVariableLimit field:
<Form.Input
label='Persist Variables Limit'
placeholder='Persist Variables Limit'
name='persistVariablesLimit'
value={item.persistVariablesLimit || ''}
onChange={this.handleChange}
required={false}
disabled={false}
error={this.state.errors['persistVariablesLimit']}
/>
This is an item originally retrieved from an API. It's initialized as follows:
emptyItem = {
fullName: '',
contact: '',
requester: '',
tag: '',
company: '',
companyId: '',
product: '',
expiration: '',
macsArray: '',
dashboardIDs: '',
typeId: '',
family: '',
systems: '',
fileName: '',
url: '',
attributesArray: [],
persistVariablesLimit: '',
dataSourceLimit: ''
}
constructor(props) {
super(props)
const { cookies } = props
this.state = {
item: this.emptyItem,
csrfToken: cookies.get('XSRF-TOKEN'),
fields: {},
errors: {}
}
...
}
Here's the API call:
if (this.props.match.params.id !== 'new') {
try {
const tmpLicense = await (await fetch(API_HOST + `/api/license/${this.props.match.params.id}`, { credentials: 'include' })).json()
this.setState({ item: tmpLicense })
} catch (error) {
this.props.history.push('/')
}
How should I be setting this default value? What is the issue here?
You’re setting the default values as numbers, .trim is a String method.
It should be:
if (!item.persistVariablesLimit) {
item.persistVariablesLimit = '50';
}
if (!item.dataSourceLimit) {
item.dataSourceLimit = '50';
}

React-Redux update part of the state

I have the following initial state for my react-redux
const initialState = {
logged: false,
user: null,
company: null,
}
When I first login, I set the state data
const data = action.payload.data;
const properties = action.payload.properties;
const notifications = action.payload.notifications;
return {
...state,
logged: true,
user: {
id: data.UserID,
firstName: data.UserFName,
lastName: data.UserLName,
email: data.UserEmail,
securityLevel: parseInt(data.SecurityLevelID),
notifications: {
multiProp: notifications.MultiProp && parseInt(notifications.MultiProp) === 1 ? false : true
}
},
company: {
id: data.CompanyID,
name: data.CompanyName,
email: data.ContactEmail,
leadSource: parseInt(data.LeadSourceCompanyID)
}
}
Then, at some point of my project, I need to update only user.notifications.multiProp, so I created a new Type called UPDMULTIPROP and I'm doing:
case Types.UPDMULTIPROP:
const userData = action.payload.data;
return {
...state,
user: {
notifications: {
multiProp: userData.notifications.MultiProp && parseInt(userData.notifications.MultiProp) === 1 ? false : true,
}
}
}
However, it set all the other user state to undefined. How can I update just the multiProp?
Thanks
user is also an object, to keep user state the same you should also spread the user.
case Types.UPDMULTIPROP:
const userData = action.payload.data;
return {
...state,
user: {
...state.user,
notifications: {
multiProp: userData.notifications.MultiProp && parseInt(userData.notifications.MultiProp) === 1 ? false : true,
}
}
}
To update nested object immutably you need to spread it again. Like
user : { ...state.user, notifications : someValue }
Like this.

React state manipulation through spread operator

I have this initial state:
this.state = {
formInfo: {
name: '',
age: '',
height: ''
},
errorMessage: ''
}
When editing the form, the state should update and this is how I'm handling this at the moment:
handleInputChange = e => {
const { name, value } = e.target
this.setState({
...this.state,
formInfo: {
...this.state.formInfo,
[name]: value
}
})
}
Can you provide me of a better solution to manipulate the state, in case this process does not follow the industry standards?
I doubt if there's a more efficient way to archive this.
If you are asking about best practice then below is the one. Your code was fine. Only better to avoid dot notation's and provide default values.
handleInputChange = e => {
const {
target: {
name = '',
value = '',
} = {},
} = e;
const {
formInfo,
} = this.state;
this.setState({
formInfo: {
...formInfo,
[name]: value,
},
});
}

Cannot call setState in callback

I am new to React, so I know I might not be doing everything right.
I am trying to call setStatus inside of a callback for onreadystatechange but getting an error in the browser. The code does an AJAX call to the server to see if a proposed username is already in the database. Upon return the onreadystatechange is executed. There I am trying to set the new error message for the username if it already exists.
Here is my state data:
const initialState = {
firstname: "",
lastname: "",
username: "",
password: "",
email: "",
firstnameError: "",
lastnameError: "",
usernameError: "",
passwordError: "",
emailError: ""
};
class SignUpForm extends Component {
constructor() {
super();
this.state = initialState;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.checkUserName = this.checkUserName.bind(this);
this.checkEmail = this.checkEmail.bind(this);
this.validateForm = this.validateForm.bind(this);
}
This is where I update state in a change handler for fields in the form:
handleChange(e) {
let target = e.target;
let value = target.value;
let name = target.name;
if (name === "username") {
value = value.toUpperCase();
}
this.setState({
[name]: value
});
}
This is the routine where I am getting an error in the browser. The lines where the error occurs is marked with a comment on the line below.
checkUserName() {
let usernameError = "";
let element = "";
let checkNameResponse = "";
let checkNameMessage = "";
let request = "";
let url = "";
let userName = "";
let requestData = "";
let checkNameResponseJSON = "";
request = new XMLHttpRequest();
if (request == null) alert("Unable to create checkDBForUSerNameRequest");
else {
url = "/php/CheckUsername.php";
userName = escape(document.getElementById("username").value);
requestData = JSON.stringify({
username: userName
});
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
checkNameResponseJSON = JSON.parse(request.responseText);
checkNameResponse = checkNameResponseJSON.returncode;
checkNameMessage = checkNameResponseJSON.message;
element = document.getElementById("SignupIcon");
element.className = "displayIcon";
if (checkNameResponse === 0) {
element = document.getElementById("SignupIconFile");
element.src = "/images/YesButton.png";
element.alt = "Available";
this.setState({ usernameError: "" });
} else {
element = document.getElementById("SignupIconFile");
element.src = "/images/NoButton.png";
element.alt = "Not Available";
usernameError = checkNameMessage;
this.setState({ usernameError: usernameError }); // BROWSER ERROR
}
}
};
request.open("POST", url, true);
request.setRequestHeader("Content-Type", "application/json");
request.send(requestData);
}
}
This is the text from the browser error:
0: Object doesn't support property or method 'setState'
Any ideas why this happens or how to fix?
The error is because you are using request.onreadystatechange = function() {} instead of request.onreadystatechange = () => {} or request.onreadystatechange = function() {}.bind(this)
When you use function(), the scope of this changes.
Remember that function() {} and () => {} are not the same.
but function(){}.bind(this) and () => {} are the same.
You need to bind the onreadystatechange function using .bind or arrow function since when the function is executed it needs to access the correct this value referring to the class context.
request.onreadystatechange = function() {
// all logic here
}.bind(this)
or
request.onreadystatechange = () => {
//all logic here
}

Redux action property defaulting to 'undefined' rather than an empty array

I have noticed that when setting and initial state action object in redux, that empty arrays return undefined, which is keeping me from accessing array methods like .push() or .length.
Is there a way to stop the value from simplifying down to undefined?
export const startAddCustomer = (customerData = {}) => {
return (dispatch) => {
const {
name = '',
age = 0,
address = {
address: '',
city: '',
state: ''
},
coats = [],
shoes = [],
phoneNumber = '',
dependants = [],
visits = [],
} = customerData;
const customer = {
name,
age,
address,
coats,
shoes,
phoneNumber,
dependants,
visits
};
database.ref('customers').push(customer).then((ref) => {
dispatch(addCustomer({
id: ref.key,
...customer
}))
})
}
};

Resources