Javascript. adding items to an array updates all items - javascript

This question is somewhat related to this issue I had earlier today:
Adding items to an array in javascript
It works to add items to my array now, but it seems that when I update the array all items will be the same even though the object passed into the method is different everytime
My method looks like this:
addShoe(shoe) {
console.log("Adding new shoe to collection: ");
console.log(shoe);
this.setState(
{
shoes: [...this.state.shoes, shoe]
},
function() {
console.log("Shoe collection:");
console.log(this.state.shoes);
}
);
}
So after one run, this is what the console in Chrome looks like. Which seems to be right:
When I try to add one more to the collection, this is what happens:
Now my collection contains two items which is correct, but it seems like all items in the collection has the same data?
What am I doing wrong here?
EDIT
In another React component I have the following state:
this.state = {
shoe: {
selectedBrand: "",
selectedEU: "",
selectedUS: "",
selectedUK: "",
selectedFraction: ""
}
};
Once a field is updated with a new value, the following method will be triggered:
updateSelectedValues(property, event) {
const shoe = this.state.shoe;
shoe[property] = event.value;
this.setState({ shoe: shoe });
}
When a button in this modal window is closed, the this.state.shoe will be pass as a param to method in the "parent" component.

Related

How to use spread operator in setstate react class component

I am developing a component where I will get the data from a call back function. Initially the state of the component will be empty [], later once the callback function is called I need to update the values into the state. At a time I'll recive only one array, meaning user can add one item at a time that item will consists of nested objects and array values. I have added the logic for the same to handle the scenario, but when I am testing in jest when I am trying to add another set of item from mock meaning the user can select next item when the done with selecting and submitting the first item at that time my logic is getting failed, I am not getting where I went wrong, could any one help me to resolve this issue, thanks in advance! I have added the mock data structure and logic and jest test below.
Mock:
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
}
ItemID:'123'
}
Code:
class ItemComp extends React.Component{
this.state = {
processingItems:[]
onAddItemHandle = (processingItem) => {
this.setState(prevState => ({
processingItems: [...prevState.processingItems, processingItem]
}))
}
JEST:
describe('handleonAddItem', () => {
it('should allow to add multiple items based on prevState', () => {
const compView = mountWithIntl(
<compView
itemId={12}
/>
}
const instance = compView.find(compViewComponent).instance();
instance.onAddItemHandle(items) // when I am giving only one instance my logic is working
instance.onAddItemHandle(items) //when I am giving it for second time it's failing I am getting error like expected - 0 , received +18 I want to update the items here when user clicks for second time but it is failing.
expect(instance.state.processingItems).toEqual([items])
Missing a ',' before the ItemID is the only issue I faced while reproducing.- https://codesandbox.io/s/intelligent-chaplygin-0ot56e?file=/src/App.js
const items = {
itemList: {
itemOne: [{
id: "01",
category: "It-A",
isCreated:"true"
}],
itemDesc:[{
id:"01",
type:"A-1",
isCreated:"true"
}]
},
ItemID:'123'
}

React error during button click and adding data to database

I'm trying to add data into a database and display it in a table on my front-end at the same time. Adding the data into the database is done by a button click.
The data is added to the database without any problems but when trying to display it in the table I just get some errors.The error picture is here.
The table I'm using looks like this (react bootsrap datatable)
<MDBDataTable
striped
small
data={data}
tbodyTextWhite
theadTextWhite
noBottomColumns
/>
The data variable which is an array looks like this:
const data = {
columns: [
//RANDOM COLUMNS HERE
],
rows: tableRowsData(history_numbers_user_database),
};
Example of output for rows:
{
id: "4",
nr: "50898980",
service: "ps5",
id3: "500",
delete: "yes",
},
{
id: "5",
nr: "50898980",
service: "tv",
id3: "500",
delete: "yes",
}
The tableRowsData(history_numbers_user_database) looks like this
const tableRowsData = (database_data) => {
if (!database_data) {
return [];
}
return database_data;
};
and the history_numbers_user_database looks like this:
const history_numbers_user_database = useSelector((state) => {
console.log(state);
return state.auth.user ? state.auth.user.history_phone_numbers : "";
});
On the button click data needs to be added to the history_numbers_user_database (a new row needs to be added to the table as well) in the database and it should be displayed dynamically in the front-end.
The data variable should always be an array and I'm guessing that's why I'm facing this issue but I don't understand how the button click which change the variable type or if it's something related to re-rendering.
const tableRowsData = (database_data) => {
if (!database_data) {
return [];
}
return database_data;
};
You are calling this function with a string, and in it's second case it just returns what you passed into it.
In your history_numbers_user_database selector, it returns an empty string at the end of the ternary.
So when you call tableRowsData(""), it returns a string, which is not an array, hence .filter is not a function.

Click on React list of elements and store it as a value in another list

I have this sample code which is a search input that filters and returns different colors in a list. When you click on one of the items the innerHTML from that list item then populates the value in the input field.
My question is when I click on one of the list items, I would want to get that specific list item and store it in another list. Much like a search history. Is this difficult to add to the current state of the search field? How would you suggest that I'd approach this?
Do i need to have some kind of onSubmit function to achieve this?
See this example:
https://codesandbox.io/s/autocomplete-6lkgu
Thanks beforehand,
Erik
You can create a state variable searchHistory, which can be array, and onClick of the item, you can do :
onClick = e => {
this.setState({
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText,
searchHistory: [...this.state.searchHistory, e.currentTarget.innerText],
});
};
I'm assuming you want all of the search items to be unique? You can use a set for that. Here is the code below for your AutoComplete component.
constructor
constructor(props) {
super(props);
this.state = {
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: "",
searchHistory: new Set([])
};
}
onClick
onClick = e => {
const { searchHistory } = this.state;
searchHistory.add(e.currentTarget.innerText);
this.setState(
{
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText,
searchHistory: [
...searchHistory,
searchHistory
]
},
() => {
console.log("[search history]", this.state.searchHistory);
}
);
};
You can simply create an array in your state and push each option into it inside your onClick handler. I made a simple example here that just logs the history when it changes. You can then use/display it any way you'd like.
Note that you'd also need to add similar functionality inside of your onKeyDown handler, as it appears you're also triggering a change with the enter key. I updated the fork to include this.

Deleting Record from List, Apollo GraphQL Optimistic UI Mutation

I'm looking for a way to have an optimistic Ui delete an Item from a list.
The query for the list is:
myQuery{
Foo:{
Bar:{
id
}
}
delete mutation:
mutation deleteBar(input: barInput!){
deleteBarItem: (responds Boolean for success)
}
How do I structure the optimisticResponse?
deleteBarItem({
variables:{
barInput:{
id: idToBeDeleted
}
},
optimisticResponse:{
?????
},
update: (cache, { data}) => { ...manipulate the list in cache}
})
Here's a Medium article with one example for how to use OptimisticResponse for deleting an item from a list.
It'd be helpful to see your schema, but here's a working example from a toy to-do app I built with OptimisticResponse working for deleting items from the list:
optimisticResponse: {
__typename: "Mutation",
deleteResolution: {
__typename: "Resolution",
_id
}
}
You can verify your OptimisticResponse is working by setting your Network speed to Slow 3G in the dev console and ensuring that the item still gets removed immediately.
Side note - you can use filter in the update function instead of a boolean to cleanly create a new array with the item removed. Here's how I do this in the to-do app linked above, where _id is the id of the item to be removed that gets passed in as a prop to the DeleteResolution component:
resolutions = resolutions.filter(
resolution => resolution._id !== _id
);
Thanks to xadm for providing an example in the comments to reference
deleteBarItem({
variables:{
barInput:{
id: idToBeDeleted
}
},
optimisticResponse:{
deleteBarItem: true,
foo:{
id: idToBeDeleted,
__typename: "Bar"
}
},
update: (cache, { data}) => { ...manipulate the list in cache}
})
A couple key notes.
Cache: Uses __typename and id to reference objects. Using the optimistic ui to "delete" and Item you need to set the cache with an object that has no other information associated with it.
update: This function is run twice. Once with the optimistic results and then again when the mutation comes back. With my example of the mutation returning a boolean I added a filter to the list based on that boolean to remove deleted item from the list or leave it intact.

Enyo Collection Merge Strategy

Below is the function I have used to fetch more photos from a service provider once clicked on more button:
showMore: function(){
this.$.resultList.collection.fetch({strategy:"merge",rpp:50});
}
This will call the fetch method defined in collection,
fetch: function(opts) {
this.params = {
feature: this.methodType || "popular",
image_size: 3,
sort: "created_at",
rpp: opts && opts.rpp || 25
};
return this.inherited(arguments);
},
This is working fine, but the problem is once more button is clicked, it should fetch another set of 25 photos and append it to my collection, but what actually happening is sometimes, it shows only say 5 photos along with previous photos .
What I understand by "merge" strategy is, if the records received is same as previous records, it will take only those records which are different from previously fetched records and updates the primarykey of the duplicate records.So one reason i am able to figure out is that, may be, it is fetching 25 photos next time, but because most of them are same as before, it is showing only those which are different from the previous fetched photos.
If i go with the "add" strategy, it works fine for one time, i.e, it shows another set of 25 photos appended to the collection, most of them are again same. But if click on more button one more time, no records are being added to the collection.No idea why this is happening.
How should i approach, if i want to fetch only new photos and append it to the collection.
Using the merge strategy is the right approach. Your description of merge is mostly accurate except that it doesn't update the primary key but instead updates the data for existing records with the same primary key.
It's difficult to say why using "add" doesn't always work. If the records don't have a primary key (which is id by default), "add" and "merge" should always add the records to the collection (unless you're using mergeKeys). If they do have a primary key, it's possible that you're trying to add duplicate records which Enyo will complain about and abort. Check your console log.
Without code, the only other suggestion is to set breakpoints and step through enyo.Collection.merge.
Here's an example of fetching records into a collection. If you comment out setting the id, merge and add strategies will always add records. If you comment out the merge strategy, the code will eventually error when requesting more records.
enyo.kind({
name: "ex.MockSource",
kind: "enyo.Source",
fetch: function(rec, opts) {
if(rec instanceof enyo.Model) {
rec.setObject(Faker.Helpers.createCard());
} else if (rec instanceof enyo.Collection) {
var count = opts && opts.count || 25;
var cards = [];
for(var i=0;i<count;i++) {
var card = Faker.Helpers.createCard();
// artificial id to force merges
card.id = enyo.now()%40;
cards.push(card);
}
opts.success(cards);
}
}
});
enyo.store.addSources({
mock: "ex.MockSource"
});
enyo.kind({
name: "ex.App",
kind: "Scroller",
bindings: [
{from: ".data", to: ".$.list.collection"},
{from: ".data.length", to: ".$.count.content", transform: function(v) {
return enyo.format("Displaying %s records", v);
}}
],
components: [
{name: "count"},
{name: "list", kind: "DataRepeater", components: [
{kind: "onyx.Item", components: [
{name: "name"},
{name: "phone"}
], bindings: [
{from: ".model.name", to: ".$.name.content"},
{from: ".model.phone", to: ".$.phone.content"}
]}
]},
{kind: "onyx.Button", content: "More", ontap: "moreTapped"}
],
create: enyo.inherit(function(sup) {
return function() {
sup.apply(this, arguments);
this.set("data", new enyo.Collection({
defaultSource: "mock"
}));
this.fetchRecords();
};
}),
fetchRecords: function() {
this.data.fetch({
count: 5,
strategy: "merge"
});
},
moreTapped: function() {
this.fetchRecords();
}
});
new ex.App().renderInto(document.body);

Categories