How to make validation in setState? - javascript

I have a datalist , on change I have a function , that checks value was picked from options or typed new value. Depending on that I'm assigning it to variable "address_id" or "new_address" . In the end I need to make object and store it in state. The problem is I need to put only one value that is not null.
If address_id is null, so in object should be new_address. And if new_address is null, in object address_id.
address_id=null
tempObj: {
new_address: new_address,
},
How to make validation which checks what variable is not null and put it in object.
onChooseAddress(e, idx) {
const { adresses } = this.state
let address_id = null
let new_address = null
for (let i = 0; i < adresses.length; i++) {
if (
e.target.value === adresses[i].address &&
e.target.name === "address"
) {
address_id = adresses[i].id
}
}
if (!address_id && e.target.name === "address") {
new_address = e.target.value
}
// Here I need make validation!!!
this.setState(prevState => ({
tempObj: {
...prevState.tempObj,
// address_id: address_id or new_address: new_address,
},
}))
}

You can just do a normal function that returns the state object.
this.setState(prevState => {
// do validations here
return {
tempObj: {
...prevState.tempObj,
// address_id: address_id or new_address: new_address,
};
});

Something like this?
let existingaddr = addresses.find( (address) => e.target.value ===
address.address && e.target.name === 'address' )
let tmpObj = {
...prevState.tempObj
}
if( existingaddr ){
tmpObj.address_id = existingaddr.id;
}
else{
tmpObj.new_address = e.target.value
}
this.setState(prevState => (tmpObj))
I didnt test it, but the idea is to create an object, which will have one key or the other, and then spread in into your temp object. Hopefully I understand what you were trying to accomplish.
EDIT:
I changed it up a bit and put it in a code pen. It follows your usecase as far as I can tell.

Related

why my lodash cloneDeepWith method only run once?

I have a very very very deep nested object state.
and i want to change all id properties at once with lodash cloneDeepWith methods.
i'm using cloneDeepWith and only works on first match.
if i dont return the modified object then it won't modifiy anything.
and if i return the value i think the function stops.
the function its working ok but the only problem is that only will run once.
const handleChangeIds = (value) => {
if (value === sections) {
const modifiedObject = cloneDeepWith(value, (sectionsValue) => {
if (sectionsValue && Object.hasOwn(sectionsValue, 'id')) {
const clonedObj = cloneDeep(sectionsValue);
clonedObj.id = generateObjectId();
return clonedObj;
// I Also Tried sectionsValue = clonedObj; its the same behavior
}
});
return modifiedObject;
}
};
const DuplicateSection = () => {
console.log('Original Store', form);
const store = cloneDeepWith(form, handleChangeIds);
console.log('Modified', store)
};
For those who want to achieve same thing like me.
I had a super deep nested object for form. and that form had a repeatable functionality.
and i needed to do two thing in generating another form.
generate new Id for every field Id.
clear the input Value.
I solved my problem like this
and it works perfectly for a super deep nested object.
import cloneDeepWith from 'lodash/cloneDeepWith';
const clearInputAndChangeId = (sections: FormSectionProps): FormSectionProps => {
return cloneDeepWith(sections, (value, propertyName, object) => {
if (propertyName === 'id') return generateObjectId();
if (propertyName === 'selected') return false;
if (propertyName === 'checked') return false;
if (propertyName === 'value') {
if (object.type === 'file') return [];
if (object.type === 'checkbox/rating') return 1;
return '';
}
});
};

Only push the object which is not in the array yet

This is my function:
const multiSelect = value => {
let tmpArr = [...selectedPeople];
if (tmpArr.length === 0) {
tmpArr.push(value);
} else {
tmpArr.map(item => {
if (item.id !== value.id) {
tmpArr.push(value);
} else {
return;
}
});
}
setSelectedPeople(tmpArr);
};
I want to check the array for the new value by comparing it with all items. If value === item item the loop function should return, but if the value is not in the array yet, it should push it.
This is a big problem for me but I assume it is a small problem for you guys.
Use Array.every() to check if the array doesn't contain an item with the same id:
const multiSelect = value => {
const tmpArr = [...selectedPeople];
if(tmpArr.every(item => item.id !== value.id)) {
tmpArr.push(value);
}
setSelectedPeople(tmpArr);
};
However, this means that you're duplicating the array needlessly, while causing a re-render, that won't do a thing. So check if the item is already a part of selectedPeople by using Array.some(), and if it does use return to exit the function early. If it's not continue with cloning, and updating the state:
const multiSelect = value => {
if(tmpArr.some(item => item.id === value.id)) {
return;
}
const tmpArr = [...selectedPeople];
tmpArr.push(value);
setSelectedPeople(tmpArr);
};
Use find to check if the item is already in the array. Also, there's no need to make a copy of the source array:
const multiSelect = value => {
if (!selectedPeople.find(item => item.id === value.id))
setSelectedPeople(selectedPeople.concat(value))
}
Another approach.
const
multiSelect = value => setSelectedPeople([
...selectedPeople,
...selectedPeople.some(({ id }) => id === value.id)
? []
: [value]
]);

React: useState array doesn't change when state change method called

Array state doesn't change when state change method is beign called :
const [arrayOfDocuments, setArrayOfDocuments] = useState([]);
i tried : setArrayOfDocuments(...[]); or setArrayOfDocuments([]);
where i use my method :
const pushToArrayOfDocuments = (obj) => {
const arr = arrayOfDocuments;
if (obj.filename && obj.file && obj.expiredate && obj.doctype) {
const index = arr.map((e) => e.filename).indexOf(obj.filename);
if (index !== -1) {
arr[index] = obj;
} else {
arr.push(obj);
}
setArrayOfDocuments(arr);
}
};
Maybe the problem is push? and i should do setArrayOfDocuments(...arr); or setArrayOfDocuments(prev => [...prev,...arr]) but if doing so i guess it will go in infinte rendering as i'm passing pushToArrayOfDocuments to the subcomponents.
Like this :
OperatorDocument
key={`Durc${count}`}
title="Durc"
description="Descrizione Durc"
setDocument={pushToArrayOfDocuments}
document={getObjectByName('Durc')}
filedocname="Durc"
/>
edit :
doing like this : setArrayOfDocuments([...arr]);
i get Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
Any help is appreciated.
Firstly, you should never mutate useState's state directly, use them as immutable entities. If you want to use it as initial value, clone it before:
const arr = [...arrayOfDocuments]
// or
const arr = arrayOfDocuments.slice()
Secondly, you are passing the same state array to the setter, then the state will not be updated. Cloning the state will solve this second point.
Finally, the best way to construct a new state from the old value is using a function:
setState(oldValue => (/* construct new state based on old value */))
this will avoid using a value that is not up to date.
At the end, you will have:
const pushToArrayOfDocuments = (obj) => {
if (obj.filename && obj.file && obj.expiredate && obj.doctype) {
setArrayOfDocuments(oldArr => {
const arr = oldArr.slice();
const index = arr.map((e) => e.filename).indexOf(obj.filename);
if (index !== -1) {
arr[index] = obj;
} else {
arr.push(obj);
}
return arr;
}
)
}
};
You need to clone your array before adding it to state.
const arr = arrayOfDocuments.slice();
Full snippet:
const pushToArrayOfDocuments = (obj) => {
if (obj.filename && obj.file && obj.expiredate && obj.doctype) {
const arr = arrayOfDocuments.slice();
const index = arr.findIndex(({ filename }) => filename === obj.filename);
if (index > -1) {
arr[index] = obj;
} else {
arr.push(obj);
}
setArrayOfDocuments(arr);
}
};
I add a similar problem, and I solved by
instead of
const arr = arrayOfDocuments
try spreading the initial array
const arr = [...arrayOfDocuments]

Returning an object only if all of its values are non-null

Let's say I have an object in which the value of each field is retrieved with an individual call:
let ExampleObj = {
foo: getFoo(),
bar: getBar(),
baz: getBaz(),
...
}
I have a function that will return the object if and only if all fields evaluate as non-null:
let returnsExampleObj = () => {
// if (!!ExampleObj.foo && !!ExampleObj.bar && !!ExampleObj.baz) {
// return ExampleObj
// } else {
// return undefined
// }
}
I can obviously just manually check each field to see whether it's null or not like in the above example, but is there a more elegant way of doing this? There may be dozens of fields in this object and to check each one manually would be very tedious.
Just check that .every of the values are truthy:
const returnsExampleObj = () => {
return Object.values(ExampleObj).every(Boolean) ? ExampleObj : undefined;
};

Undefined counting as a variable -- messes up with my isObjEmpty() function

I am trying to post an object only if it's not empty. However I have code which causes properties to become undefined -- and when that happens, the obj is not empty anymore and the post still happens.
userSearchData = {};
$('#addContactForm').keyup(function()
{
var email = $(this).find('input[name="email"]').val();
var username = $(this).find('input[name="username"]').val();
var fullName = $(this).find('input[name="fullName"]').val();
userSearchData.email = email.length >= 3 ? email : undefined;
userSearchData.username = username.length >= 3 ? username : undefined;
userSearchData.fullName = fullName.length >= 3 ? fullName : undefined;
console.log(userSearchData);
if ( !isEmpty(userSearchData) )
{
console.log("Is empty")
$.post( '/post/addContact', { userSearchData: userSearchData }, function( data )
{
console.log( data );
});
}
});
It's a "search" form, so if a user types for example "Blabla" as the username, and then erases letters to make it "Bl", then the username variable gets undefined, so it's not being sent when doing the post (I console log the object on the server side and the undefined variables are not considered which is good).
How can I make my variables completely removed, instead of undefined when their length is below 3?
I could probably modify the isEmpty function to return false if all keys are undefined, would that be better to do that? If so, how would you do it?
var hasOwnProperty = Object.prototype.hasOwnProperty;
function isEmpty (obj)
{
// null and undefined are "empty"
if (obj == null) return true;
// Assume if it has a length property with a non-zero value
// that that property is correct.
if (obj.length > 0) return false;
if (obj.length === 0) return true;
// Otherwise, does it have any properties of its own?
// Note that this doesn't handle
// toString and valueOf enumeration bugs in IE < 9
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) return false;
}
return true;
}
The whole thing seems rather pointless, you can just do this instead
$('#addContactForm').on('keyup', function() {
var userSearchData = {}, self = this;
$.each(['email', 'username', 'fullName'], function(_, el) {
var val = $(self).find('input[name="'+el+'"]').val();
if ( val.length > 3 ) userSearchData[el] = val;
});
$.post( '/post/addContact', { userSearchData: userSearchData }, function( data ) {
console.log( data );
});
});
Only add the properties to the object if the condition is met.
if ( username.length >=3 ) {
userSearchData.username = username;
}
if ( username in userSearchData ) {
// do stuff
}
you can delete properties in JS, but the better fix is to just make sure your code posts when it should.
if (obj === null || obj === undefined) return;
or something might help you here.
Also, for(key in obj) is old-style "iterate over prototype as well", and highly discouraged, so you probably want this instead:
var keys = Object.keys(obj);
if(keys.length === 0) ...
keys.forEach(function(key) { ... });
Do you mean you want do this?
if(!isEmpty(userSearchData)){
$.post( '/post/addContact', { userSearchData: userSearchData }, function( data )
{
console.log( data );
});
}

Categories