I'm trying to initialize my state with the redux state that I have stored. However, when I try to map through one of the lists stored in the state it just resets the values that I have inside the list instead of returning their substring values (which I want). The thing is if I print mail.substring(0, mail.length -10) I see the value that I would like to assign to the variable but after assigning the value is empty.
Here comes the strange part: if I were to assign "hello" instead of mail.substring(0, mail.length-10) then it works which could make you assume that the substring would return an empty value but as I mentioned above it does not.
I guess this might be because I create a shallow copy of the redux state but I'm not sure. Could you help me resolve this, please?
const membershipData = useSelector(getCompanyMembershipDetails);
function getInitState() {
if (membershipData !== null && membershipData !== undefined) {
const newState = { ...membershipData };
newState.members.map((m) => {
const mail = m.contact.countersignEmail;
const newVal = mail.substring(0, mail.length - 10);
m.contact.countersignEmail = newVal;
return m;
});
return newState;
} else
return {
members: [getEmptyMemberStateForId(0), getEmptyMemberStateForId(1)],
membershipRates: [
getEmptyPropertyContributionForId(0),
getEmptyPropertyContributionForId(1),
],
registrationPermissions: [],
};
}
const [membersData, setMembersData] = useState(getInitState());
It was because of the shallow copy as I thought. Using cloneDeep from lodash I made a working version:
const membershipData = useSelector(getCompanyMembershipDetails);
function getInitState() {
if (membershipData !== null && membershipData !== undefined) {
const newState = _.cloneDeep(membershipData);
newState.members.map((m) => {
const mail = m.contact.countersignEmail;
const newVal = mail.substring(0, mail.length - 10);
m.contact.countersignEmail = newVal;
return m;
});
return newState;
} else
return {
members: [getEmptyMemberStateForId(0), getEmptyMemberStateForId(1)],
membershipRates: [
getEmptyPropertyContributionForId(0),
getEmptyPropertyContributionForId(1),
],
registrationPermissions: [],
};
}
const [membersData, setMembersData] = useState(getInitState());
If you use lodash the way I did above make sure to import it the following way:
import _ from "lodash";
Related
Let's take this Update State example:
const initialState = [
{id: 1, country: 'Austria'},
{id: 2, country: 'Belgium'},
{id: 3, country: 'Canada'},
];
const [data, setData] = useState(initialState);
const updateState = () => {
setData(prevState => {
const newState = prevState.map(obj => {
if (obj.id === 2) {
return {...obj, country: 'Denmark'};
}
return obj;
});
return newState;
});
};
1. Is it also valid to update the state like this? (First example)
const updateState = () => {
const newState = data.map(obj => {
if (obj.id === 2) {
return {...obj, country: 'Denmark'};
}
return obj;
});
setData(newState);
};
2. Is it also valid to update the state like this? (Second example)
const updateState = () => {
setData(prevState => {
const newState = prevState.map(obj => {
if (obj.id === 2) {
let newObj = obj;
newObj.country = 'Denmark'
return newObj;
}
return obj;
});
return newState;
});
};
3. Do this specific versions also have performance impacts? Which one is the best?
The first and the second example are perfectly valid. I would, however, suggest you to use the first one and I will explain why:
With the first example you are using a callback as an argument of the function. And this form means that you are actually getting the last data state value (this is important because the state updates happen asynchronously). Whenever you need to update the state based on the previous value even React suggests to use the callback form to avoid side effects.
More infos here: https://reactjs.org/docs/hooks-reference.html#functional-updates
The third example is not valid because you are mutating directly the state. Something that in react is not allowed.
More infos here: https://dev.to/il3ven/common-error-accidentally-mutating-state-in-react-4ndg
My first Object
const [message, setMessage] = useState({
Features: {Active:false},
General: {FileTimeout:0,ZipDepth:0}
});
How do I update this object?
const handleInput=e=>{
const name=e.currentTarget.name;
const value=e.currentTarget.value;
var temp = {...message}
if(name == 'active'){
if(value==='on'){
temp.Features.Active=true;
}
else{}
}
else if(name == 'timeout'){
temp.General.ZipDepth= 5;
}
else if(name == 'zipdepth'){
temp.General.FileTimeout= 7;
}
}
New Values= { Features :{Active:true}, General: {FileTimeout:7,ZipDepth:5}});
How can I update the values like this? If there is a library or something for this, I can also use it.
You are mutating your state object. Even though you create a copy temp of the message state, you are mutating the nested properties. You necessarily need to also shallow copy all nested state you are updating.
I would suggest using a functional state update to access the previous messagestate, and use aswitchstatement to cover the different cases on thename` value, returning the next state value for each one. Notice that each level of nested state is shallow copied before it's updated.
const [message, setMessage] = useState({
Features: { Active: false },
General: { FileTimeout: 0, ZipDepth: 0 }
});
const handleInput=e=>{
const { name, value } = e.currentTarget;
setMessage(message => {
switch(name) {
case 'active':
if (value === 'on') {
return {
...message, // shallow copy of state
Features: {
...message.Features, // shallow copy nested state
Active: true,
},
};
} else {
// ? return some new state
}
case 'timeout':
return {
...message, // shallow copy of state
General: {
...message.General, // shallow copy nested state
ZipDepth: 5,
},
};
case 'zipdepth':
return {
...message, // shallow copy of state
General: {
...message.General, // shallow copy nested state
FileTimeout: 7,
},
};
default:
return message; // no update, return current state
};
});
}
const [message, setMessage] = useState({
Features: {Active:false},
General: {FileTimeout:0,ZipDepth:0}
});
const handleInput=e=>{
const name=e.currentTarget.name;
const value=e.currentTarget.value;
var temp = {...message}
if(name == 'active'){
if(value==='on'){
temp.Features.Active=true;
}
else{}
}
else if(name == 'timeout'){
temp.General.ZipDepth= 5;
}
else if(name == 'zipdepth'){
temp.General.FileTimeout= 7;
}
setMessage({...temp}) // You need to call setMessage function in order to update the state.
}
I have the following code. The code is complaining about Use object destructuring.eslint(prefer-destructuring) as shown in image in red marks. How i can solve this issue ? I had a look here but https://eslint.org/docs/rules/prefer-destructuring not sure where i am doing wrong ?
GET_GEOCODE_FROM_ZIPCODE(state, action) {
const { res } = action;
if (res && res.address && res.zipcode) {
const zipcode = res.zipcode;
const address = res.address;
return {
...state,
geoCode: {...action.res, address},
zipcode,
showCallout: true
}
}
return state
}
You have to destructure your object :
const { address, zipcode } = res;
Eslint wants you to use destructuring:
const { zipcode, address } = res;
You can read more about object destructuring here:
I'm setting the data from api in two state variables both values are same.But when I am updating one state variable the another state variable also changing.
Api.get("url")
.then((response) => {
this.setState(
{
deliveryInfoSection2: Object.assign({}, response.data),
deliveryInfoSection2copy: Object.assign({}, response.data),
}
);
})
updateState() {
try {
newVal = { ...this.state.deliveryInfoSection2 };
newVal.orderDetails[index].bo = value.replace(global.REG_NUMBER_ONLY, '');
//After this state variable deliveryInfoSection2copy is also updating.
this.setState({ deliveryInfoSection2: newVal }, () => {
if (this.state.deliveryInfoSection2.orderDetails[index].bo != '') {
}
catch (e) {
alert("error" + e)
}
}
This is a issue with respect to shallow copy of variables while using spread operator in javascript. It has nothing to do with react's setState. The spread operator creates a shallow copy for the object.
response = {
orderDetails: [
{
bo: "tempData1"
},
{
bo: "tempData2"
}
]
}
deliveryInfoSection2 = Object.assign({}, response)
deliveryInfoSection2Copy = Object.assign({}, response)
//Here spread operator will create shallow copy and so, the references are copied and hence any update to one will update other.
newVar = { ...deliveryInfoSection2 }
newVar.orderDetails[0].bo = "newValue"
deliveryInfoSection2 = newVar
console.log("deliveryInfoSection2", deliveryInfoSection2)
console.log("deliveryInfoSection2Copy", deliveryInfoSection2Copy)
To fix this, you need to create a deep copy of your object.
You can use JSON.parse(JSON.stringify(object)) for the same.
response = {
orderDetails: [
{
bo: "tempData1"
},
{
bo: "tempData2"
}
]
}
deliveryInfoSection2 = Object.assign({}, response)
deliveryInfoSection2Copy = Object.assign({}, response)
//This will create a deep copy for the variable
newVar = JSON.parse(JSON.stringify(deliveryInfoSection2))
newVar.orderDetails[0].bo = "newValue"
deliveryInfoSection2 = newVar
console.log("deliveryInfoSection2", deliveryInfoSection2)
console.log("deliveryInfoSection2Copy", deliveryInfoSection2Copy)
Hope it helps. Revert for any doubts/confusions.
i have a method set_data which is used to set data based on id. I know it could be easy to call this set_data in componentdidupdate when id changes. However in doing so it doesnt set some state variables in the parent component.
To get rid of that want to call set_data method in render . However since this set_data method sets state of data it enters into an infinite loop in render . Also cannot provide a condition (like prevprops.id!== this.props.id) to execute set_data method.
To prevent it thought of using this set_data method not to set state at all. and can call this set_data method in render.
Below is the code,
export default class child extends React.Component {
state = {
query: '',
data: null,
};
empty_id = 0xffffffff;
componentDidMount() {
this.set_open_data();
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.set_data();
}
}
set_data = () => {
if (!this.props.info) {
return;
}
if (this.props.id === this.empty_id) {
this.setState({data: null});
return;
}
let data = {
info: [],
values: [],
};
const info = this.props.info;
for (let i=0, ii=info.length; i < ii; i++) {
if (info[i].meshes.includes(this.props.id)) {
const info = info[i].info;
const values = info[i].values;
data = {
info: typeof info === 'string' ? info.split('\r\n') : [],
values: values ? values : [],
};
break;
}
}
this.setState({data: this.filter_data(data, this.state.query)});
};
render = () => {
const shown_data= this.state.data;
/* i want to call set_data method here*/};}
Could someone help me solve this. Thanks.
You can't call setData there, because that would be anti-pattern. It will trigger a loop that will continuously render as well as keeps setting state.
You can probably rewrite the component this way:
export default class child extends React.Component {
state = {
query: ''
};
empty_id = 0xffffffff;
componentDidMount() {
this.set_open_data();
}
set_data = () => {
let data = {};
if (!this.props.info) {
return data;
}
if (this.props.id === this.empty_id) {
return data;
}
let data = {
info: [],
values: [],
};
const info = this.props.info;
for (let i=0, ii=info.length; i < ii; i++) {
if (info[i].meshes.includes(this.props.id)) {
const info = info[i].info;
const values = info[i].values;
data = {
info: typeof info === 'string' ? info.split('\r\n') : [],
values: values ? values : [],
};
break;
}
}
data = this.filter_data(data, this.state.query);
return data;
};
render = () => {
const shown_data= this.state.data;
const data = this.set_data();
/* i want to call set_data method here*/};}
In this, we are not setting data in the state. For every new ID, it will get new data and will compute it from render thereby avoiding antipattern. I have also removed componentDidMount, since we are doing computation in render. Note: This solution means taking away data from the state, if you are not using data anywhere before render, this will work.
Let me know if this helps.