handleChange event Typescript - reactjs

I am trying to implement search bar but it seems it doesn't work.
const [dataCoupons, setDataCoupons] = useState<Coupon[] | undefined>(coupons);
const handleChange = (e: ChangeEvent<HTMLInputElement> & { length: number }) => {
let couponsFilter = dataCoupons;
if (e.length > 0) {
Object.keys(e).forEach((k) => {
if (e[k].value === 0) {
couponsFilter = couponsFilter
?.slice()
.filter((x) => x.durationType.localeCompare('ONCE') === 0);
}
if (e[k].value === 3) {
couponsFilter = couponsFilter?.slice().filter((x) => x.valid === true);
}
if (e[k].value === 4) {
couponsFilter = couponsFilter?.slice().filter((x) => x.valid === false);
}
});
} else {
couponsFilter = coupons;
}
setDataCoupons(couponsFilter);
};
<Select
onChange={handleChange}
isMulti
options={options}
isClearable={isClearable}
classNamePrefix='select'
/>
I have an error message for each e[k]
What am i missing ?

There are two main things stopping you from getting value.
You're protecting with if (e.length > 0) {}
e does not normally have length property because it is an object. If you watched closely, TypeScript does tell you that. It is why you had to add & { length: number } to stop TypeScript from complaining.
If you were to do something like below (without the above mentioned protection)
Object.keys(e).forEach((k) => { console.log(k) })
And then went and triggered change event, you would be able to observe that the console is printing out string keys of all the properties that are attached to e object directly.
One of those properties is target property. You are most likely after that property. Going along the lines of your thinking (how you wrote your code), you would most likely need something like below:
Object.keys(e).forEach((k) => {
if (k === "target") {
console.log(e[k].value);
}
});
The above will capture value from the input field.
I have to warn you though that it seems like a very unsustainable way of creating forms and getting their values. It would be probably best to read this before you proceed.
Also, here's Sandbox to see it in action in most simple implementation.

Related

Cypress assert is element inside array

Can someone please, assist in following:
Short explanation: opened one page, taken text element, then opened second page nd among 4 or 5 elements, need to assert that element from page one, is inside created array of those several elements. Wrote this code:
Cypress.Commands.add(
'assertForOpenedElementVisible',
(list1, list2, notDisplayedElementMsg) => {
const textsArray = []
cy.get('body').then((body) => {
if (body.find(list1).length > 0) {
cy.get(list1).each(($el, index) => {
const text1 = $el.text().replace(', ', '')
cy.get(list1).eq(index).click()
cy.wait(1000)
cy.get(list2)
.each(($el, index) => {
const text = $el.text().replace(', ', '')
textsArray.push(text)
cy.log(textsArray)
cy.log(text)
})
.then(() => {
cy.wrap(expect(textsArray).to.include(text1))
})
})
} else {
cy.log(notDisplayedElementMsg)
}
})
}
)
And when check Test runner - I got elements, but test fails:
How to correctly assert that? Thank you in advance
You can do an assertion like:
expect(text1).to.be.oneOf(textsArray)
OR, you can directly assert without using each() as well like:
cy.get(list2).should(($list2) => {
expect($list2.eq(3)).to.contain('49') //If you know the position
})
cy.get(list2)
.invoke('text')
.then((text) => {
expect(text).to.contain('49') //If you don't know the position
})

Nested onSnapshot problem in react-native-Firebase Firestore

I'm struggling to overcome problems that I have on nested onSnapshot, so, in short, I have 3 nested onSnapshot and when parent/root onSnapshot updates it also creates new onSnapshot listeners and leaves old ones too, so old and new ones are listening to the changes. I know that I should unsubscribe it but I can't, I'm losing track of which listeners are added or already exist.
One solution is to create array of unsubscribing functions in parent onSnapshot, but there another problem comes, I'm using docChanges with forEach and it is hard to manage which would I unsubscribe from array.
I saw this on Stackoverflow but it doesn't fit mine or even doesn't explain correctly exactly this case: nested listeners
What can you suggest to me? I don't know what else should I do.
Thanks in advance.
here is an example of my code that I'm trying to implement unsubscribe stuff( I use Mobx):
// TODO: optimise this query
const id = auth().currentUser?.uid;
// get all channelId-s that user has
channelParticipantsRef
.where('user.id', '==', id)
.onSnapshot((userChannels) => {
userChannels?.docChanges().forEach(function (channelParticipant) {
const channelParticipantData = channelParticipant;
if (!channelParticipantData.doc.exists) return;
// get all channels data that user has
channelsRef
.where(
'channelId',
'==',
channelParticipantData.doc.data().channelId,
)
.onSnapshot((channels) => {
if (!channels || channels.empty) return;
channels.docChanges().forEach(function (channel) {
const channelDataObject = channel.doc.data();
console.logBeauty(channel.type, 'channelDataObject ');
if (channel.type === 'added' || channel.type === 'modified') {
const channelData: ChannelTransformedDataType = {
...channelDataObject,
language: channelParticipantData.doc.data().language,
channelParticipantId: channelParticipantData.doc.id,
lastMessageDate: {
...channelDataObject.lastMessageDate,
},
otherUsers: [],
};
// get all channels users
channelParticipantsRef
.where('user.id', '!=', id)
.where('channelId', '==', channelDataObject.channelId)
.onSnapshot((channelParticipants) => {
const participants: UserModelType[] = [];
if (channelParticipants.empty)
return self.removeChannel(
channelDataObject.channelId,
);
channelParticipants.docs.forEach(
(channelParticipant) => {
participants.push(channelParticipant.data().user);
},
);
channelData.otherUsers = participants;
console.log(channelData, 'channelDatachannelData');
if (channel.type === 'added')
self.pushChannel(channelData);
else self.editChannel(channelData);
});
} else if (channel.type === 'removed') {
self.removeChannel(channelDataObject.channelId);
}
});
});
});
});

How to handle null values on first render?

I have a function which executes when the dropdown has a value of add or remove it will execute accordingly. However on the first render after I click the button it shows as 'added data'null. When I do it again it works. What would be the best way to handle this?
Here is my code:
Since I can't see any of the other code and there is no sandbox, I can only help you fix the issue of checking for null, however if we could see the component this function is in I could answer a lot more, however here is how you can check for null
const addtoproduction = () => {
let databaseChanges = unitTestsData.map(tests => {
return {
"unit_test_id": tests.unit_test_template_id,
"databases": tests.databases
}
})
setAddRemoveDatabaseChanges(databaseChanges)
if(AddRemoveDatabaseMode !== null && AddRemoveDatabaseMode === 'ADD'){
console.log('added data', AddRemoveDatabaseChanges);
} else if (AddRemoveDatabaseMode !== null && AddRemoveDatabaseMode === 'REMOVE') {
console.log('removed data', AddRemoveDatabaseChanges )
}
}

In React hooks, how do I refer to a generic attribute of an object in my state I want to change?

I'm using React hooks for my React 16.13.0 application. I'm trying to write a generic function to update an attribute of a complex object in my state.
const [coop, setCoop] = React.useState(props.coop);
My form contains elements like below
<Input
inputType={"text"}
title={"Name"}
name={"name"}
value={coop.name}
placeholder={"Enter cooperative name"}
handleChange={handleInput}
errors={errors}
/>
...
<Input
inputType={"text"}
title={"Street"}
name={"coop.addresses[0].formatted"}
value={coop.addresses[0].formatted}
placeholder={"Enter address street"}
handleChange={handleInput}
errors={errors}
/>
I've tried to write the following functions, but I don't know how to refer to a generic attribute of my "coop" state in my function.
const handleInput = (e) => {
let self = this;
let value = e.target.value;
let name = e.target.name;
if (name.indexOf("[") === -1) {
console.log("updating " + name + " with value:" + value);
setValue(coop, name, value);
} else {
const keys = name.split(/[\[\].]+/);
setCoop(updateValue(coop, keys, value));
}
};
const updateValue = (obj, name, value, index = 0) => {
if (name.length - 1 > index) {
const isArray = Array.isArray(obj[name[index]]);
obj[name[index]] = this.updateValue(
isArray ? [...obj[name[index]]] : { ...obj[name[index]] },
name,
value,
index + 1
);
} else {
obj = { ...obj, [name[index]]: value };
}
return obj;
};
...
const setValue = (obj, is, value) => {
console.log("setting " + is + " of value: " + value);
if (typeof is == "string") return setValue(obj, is.split("."), value);
else if (is.length === 1 && value !== undefined) {
return setCoop({ coop: obj[is[0]] = value });
} else if (is.length === 0) return obj;
else return setValue(obj[is[0]], is.slice(1), value);
};
Having trouble with this line
setCoop({ coop: obj[is[0]] = value });
How do I refer to a generic attribute of "coop" and its value?
This question is not related to hooks or React, its JavaScript.
Basically you asking how to set a generic key in an object.
You should use utility function for this as its not trivial solution, see lodash.set
Sets the value at path of object. If a portion of path doesn't exist, it's created. Arrays are created for missing index properties while objects are created for all other missing properties. Use _.setWith to customize path creation.
const object = { 'a': [{ 'b': { 'c': 3 } }] };
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4
_.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5
For your specific case, supposing obj[is[0]] is the path you can set the state with it.
But be sure to not mutate state, treat it as immutable i.e use _.set on copy of state.

How can I change a data parameter in Angular 2?

I have a problem changing a data parameter in my component file:
this.commandList.ListesCommandesATransmettre.forEach(data => {
this.catalogs.forEach(catalog => {
if (catalog.Libelle === data.Catalogue) {
if (catalog.selected === false) {
console.log(data.isSelected)
data.isSelected = false;
console.log(data.isSelected)
console.log(data);
}
}
})
});
This code displays:
true
false
But the value of isSelected in console.log(data) is still true. So, why is the result false?
Try this if it's a loop problem with the callback
for (let data of this.commandList.ListesCommandesATransmettre) {
for (let catalog of this.catalogs) {
if (catalog.Libelle === data.Catalogue) {
if (catalog.selected === false) {
console.log(data.isSelected)
data.isSelected = false;
console.log(data.isSelected)
console.log(data);
}
}
}
}
I cant understand logic of your code. What happen if this.catalogs has many catalogs that meet the condition? isSelected will be setted many times in false.
Maybe this works (if I understand your logic):
this.commandList.ListesCommandesATransmettre = this.commandList.ListesCommandesATransmettre.map(data => {
const mutateData = Object.assign({}, data);
// If condition math, then isSelected set in false. Else, isSelected keep the his value
mutateData.isSelected =
this.catalogs.some(catalog => catalog.Libelle === data.Catalogue && !catalog.selected) ? false : mutateData.isSelected;
return mutateData;
})
Try this code to test if it is updating correctly. the loop will tell which are true/false and you can see if that is the case in the updated data.
this.commandList.ListesCommandesATransmettre.forEach((data, index) => {
this.catalogs.forEach(catalog => {
if (catalog.Libelle === data.Catalogue) {
if (catalog.selected === false) {
data.isSelected = false;
}
console.log(index + 'is selected: ' + data.isSelected)
}
})
});
console.log(this.commandlist);
Something I have noticed is that web browsers will only keep the the 'most current' reference of an object in memory. So after a loop has ended, the 'most current' reference is the last one to fire. So you may be looking at your last object and not the one you are trying to test. If someone else knows more, or has a reference to what is actually happening, please edit this post.

Resources