React issues in setting a state object - javascript

I have the following code to set some dropdown state in my app.
let myFieldStatusDropdown = [];
let myFieldStatus = {};
const [myCriteria, setMyCriteria] = useState({
myFieldStatus: myFieldStatus?.value,
myFieldStatusDropdown: myFieldStatusDropdown
});
if (myContextData.myCriteria?.myFieldStatus) {
setMyCriteria({
...myCriteria,
myFieldStatus: myContextData.myCriteria?.myFieldStatus
});
} else {
setMyCriteria({
...myCriteria,
myFieldStatus: myCriteria.myFieldStatusDropdown[0]
});
}
myFieldStatusDropdown is an array like below and myFieldStatus is an object which is one of the options from it
[
{
code: 'Select',
value: 'Select'
}, {
code: 'AA',
value: 'A 1'
}, {
code: 'BB',
value: 'B 1'
}
]
For some reason, myFieldStatus is not getting set properly. Is there some issue in the way I am setting the same ?

Related

Not able to save object value in Angular component

This is component class for example:
export class AppComponent {
categories = {
country: [],
author: []
}
constructor(){}
getOptions(options) {
options.forEach(option => {
const key = option.name;
this.categories[key].push(option.value);
})
}
}
On clicking a button, I am calling getOptions(options) from different component. The structure of options looks like:
options = [
{name: 'country', value: 'Germany'},
{name: 'author', value: 'Franz Kafka'}
]
So now the value of this.categories will get updated, so now:
this.categories[country] = ["Germany"]
this.categories[author] = ["Frank Kafka"]
Value of options changes every time on clicking the button. When I am sending new options value such as:
options = [
{name: 'country', value: 'Japan'},
{name: 'author', value: 'Masashi Kishimoto'}
]
Old value for this.categories[country] is not getting saved for some reason. The new value for this.categories[country] should be ["Germany, "Japan"] but I am getting only ["Japan"] in the array.
I don't seems like the code is alright, even I tried through Javascript. What I can suggest is try checking the truthy value of options.value. It could be a possibility that there is some entry with no value options.
let categories = {
country: [],
author: []
}
let options = [
{name: 'country'},
{name: 'author', value: 'Franz Kafka'},
{name: 'country', value: 'Japan'},
{name: 'author', value: 'Masashi Kishimoto'}
]
options.forEach(option => {
const key = option.name;
console.log(key);
if(option.value)
categories[key].push(option.value);
})
console.log(categories);

React state update issue

I try to add new arrays and values in a state.
I'm using react-beautiful-dnd to display this state datas.
my initial state :
const initialData = {
users: {
'user-1': { id: 'user-1', name: 'John'},
'user-2': { id: 'user-2', name: 'Patrick'},
'user-3': { id: 'user-3', name: 'Malorie'},
'user-4': { id: 'user-4', name: 'Eric'},
'user-5': { id: 'user-5', name: 'Bob'},
'user-6': { id: 'user-6', name: 'Blob'}
},
areas: {
'area-0': {
id: 'area-0',
title: 'Main Area',
userIds: ['user-1', 'user-2','user-3', 'user-4','user-5', 'user-6']
},
'area-1': {
id: 'area-1',
title: 'Area 1',
userIds: []
},
'area-2': {
id: 'area-2',
title: 'Area 2',
userIds: []
}
},
areaOrder: ['area-0','area-1', 'area-2'],
}
class MyClass {
constructor() {
super();
this.state = {
data: initialData,
}
}
...
}
I have a dropdown menu to choose the number of areas I want to display in total, when I trigger it, I try to add the new areas in the 'areas' array and in 'areOrder' array.
If I update the number of areas again, I need to reset the state to 'initialData'.
the code in MyClass:
// apply is triggered by the dropdown menu
apply = (numOfAreas) => {
// clear state to initial data
this.setState({
data: initialData
})
for (let i = 3; i <= numOfBOR; i++ ) {
this.addNewArea(i);
}
}
addNewArea = (newRoomId) => {
const areas = { ...this.state.data.areas };
let p = "area-";
let key = newAreaId;
const newAreaKey = p.concat(key);
const areaTitle = "Area ".concat(newAreaId);
let obj = [];
obj[newAreaKey] = { id: newAreaKey, title: areaTitle, userIds: [] };
const currentAreas = { ...areas };
const newAreaObj = Object.assign(currentAreas, obj);
const newState = {
...this.state.data,
areas: newAreaObj,
areaOrder: [...this.state.data.areaOrder, newAreaKey]
};
this.setState({data: newState});
};
When I use the code above, only the last area is displayed(i.e. when I chose 8 areas, the area 8 is display after area 2)
I'm aware that setState is asynch, so I'd like to know which method will allow me to do what I want.
In your case setState doesn't use previous state, so each setState change areas to its initial value plus one new element. You can use this.setState(prevState => newState) to get previous state.

Why the value in this array (apparently) changes immediately

I couldn't find any similar question, I don't know what could be happening there, (and maybe could be something stupid) but I haven't found any clue about what could be happening.
I have this array:
const superVillains = [
{ value: '1', label: 'Thanos' },
{ value: '2', label: 'The Joker' },
{ value: '3', label: 'Ultron', disabled: true },
{ value: '4', label: 'The Riddler' },
{ value: '5', label: 'Lex Luthor' },
{ value: '6', label: 'Green Goblin' },
{ value: '7', label: 'Bain', disabled: true },
{ value: '8', label: 'The Penguin' },
{ value: '9', label: 'Doctor Octopus' },
{ value: '10', label: 'Poison Ivy' },
{ value: '11', label: 'Magneto' },
{ value: '12', label: 'Mr. Glass' },
{ value: '13', label: 'General Zod' },
{ value: '14', label: 'Red Skull', disabled: true },
{ value: '15', label: 'Baron Von Zemo' }
];
I copied this array into another called optionsState in a react state
const [optionsState, setOptionsState] = useState(superVillains);
and applied the following operations:
const index = 0;
optionsState[index]['selected'] = true;
console.log(optionsState[index]['selected']);
console.log(optionsState[index]);
console.log(optionsState);
This is the result in console:
In the first console output it seems that the selected value is true as it should, the same for the second console output, but without changing nothing in the code the third console output shows that the selected value is false.
the question is: Why does the selected value apparently changes without applying any operations on it (besides a console log statement)?
If a place another
console.log(optionsState[index]);
after the last console log it will show the same as before:
{value: "1", label: "Thanos", selected: true}
so I don't know if it is an issue with the browser or an issue with the react states or an issue with me.
Any ideas on this?
Now that you edited your question, I get it :)
You are mutating your state, and react is based in immutability (google for more), a quick fix would be :
setOptionsState(prevState => {
const oldOptions = [...prevState.optionsState];
oldOptions[index] = { ...oldOptions[index] , selected: true };
return { oldOptions };
})
As already said, you're directly mutating the state, which is a bad idea in React. To properly update the state use functional form of setState plus map:
const index = 0;
setOptionsState(options => {
return options.map((option, i) => {
if (index === i) {
return {
...option,
selected: true
}
}
return option;
})
})
optionsState[index]['selected'] = true;
You should not mutate state like this. If you want to add a new attribute to the state, you should do it using the function provided, in your case it is going to be setOptionsState.
The easiest way to achieve this should be something like this:
setOptionsState(prevState => {
const oldOptions = [...prevState.optionsState];
oldOptions[index] = { ...oldOptions[index], selected: true };
return { oldOptions };
})
Please try this approach and see if it's any different (I hope so!)
if you want to change the value of optionsState use setOptionsState because the state value is updated after the refresh if you dont use setOptionsState.

How to search within the nested array of objects in javascript?

i want to search a for a string say for example "hello" within 'name' in array of objects. The data structure is like the one below,
[{name:'hello', value: 'value', children: [{ name:'world', value: 'something'}]},
{name:'somename', value: 'value2', children: [{name: 'hello', value: 'value3', children: [{name: 'anothername', value: 'value4'}]},
{name: 'new', value: 'value5'}];
So from the above mentioned data structure, if the search query is say 'hello' i want to check through 'name' field of each object. also check within children 'name' field of each object. Some object may or may not have children and nested children too. I want to retreive the objects that have matched the search query.
For example if my search query is 'hello' then the expected output is as below,
[{name:'hello', value: 'value', children: [{ name:'world', value: 'something'}]},
{name:'somename', value: 'value2', children: [{name: 'hello', value: 'value3', children: [{name: 'anothername', value: 'value4'}]},
i have tried using the below search method but that doesnot search within children and nested children.
search = (query, listitems => {
if (!query) {
return listitems;
}
query = query.toLowerCase();
const results = [];
let counter;
let childcounter;
listitem.forEach((listitem) => {
counter = 0;
childcounter = 0;
if (listitem['name'].toLowerCase().indexOf(query) > -1)
{
counter++;
if (listitem.children)
{
listitem.children.forEach ((child) => {
if (child['name'].toLowerCase().indexOf(query) > -1)
childcounter++;
});
}
listitem.counter = counter;
listitem.childcounter = childcounter;
results.push(listitem);
}
return result
});
How can i do it. could someone help me with it. Thanks.

aurelia search filtering using a computed is causing issues when rendering to view

I have a search function that retrieves objects
this.searchForObjectByName = () => {
this.server.get('objects/?searchTerm=' + self.searchTerm)
.then(groupObjects=>
{
this.searchResults = groupObjects;
});
}
I then set the result of this to a property called searchResults, which are a groups of objects.
I now want to perform some filtering base on the results. Say I have some groups of objects that look like:
{
id: 1,
name: 'group 1',
objects: [
{
id: 1,
name: 'object name 1',
categories: ['Cat A', 'Cat B']
},
{
id: 2,
name: 'object name 2',
categories: ['Cat A', 'Cat D']
},
{
id: 3,
name: 'object name 3',
categories: ['Cat C', 'Cat D']
}
]
},
{
id: 2,
name: 'group 2',
objects: [
{
id: 4,
name: 'object name 4',
categories: ['Cat A', 'Cat F']
},
{
id: 5,
name: 'object name 5',
categories: ['Cat C', 'Cat D']
}
]
}
I would like to then filter to only show Cat A objects.
I have a computed that filters the search results based on selected categories:
get filteredSearchResults(): any[] {
var filteredGroups = this.searchResults.map(res => {
let filteredGroup = new Group;
filteredGroup = res.id,
filteredGroup = res.name;
filteredGroup = res.objects.filter(o => this.filteredCategories.some(fc => o.categories.some(oc => oc == fc)));
return filteredGroup;
});
filteredGroups = filteredGroups.filter(fg => fg.objects.length > 0);
return filteredGroups;
}
This filters the results fine and I can select different categories and it filters correctly. However it jumps all over the place in the UI, the results are constantly updating due to using a computed to filter the results. I can't use the computedfrom attribute because its based on the filtered categories I've selected (that are an array) and the results returning from the server (also an array). Has anyone else experienced this issue before? Everything I try causes jitter and I'm not sure how I can do this differently.
I could trigger events when selected categories change or when data is returned from the server, I could then recalculate the filtered results. This would work but is hacky and would prefer to be able to do this in a computed
Without computedFrom, your property getter is being polled, which is why your interface is jumping around. I'm not familiar with .some, but I'm guessing order is not guaranteed. While you could simply order your search results to work around this, it would be pretty inefficient to be filtering and sorting an array every 5 seconds.
Using a collection observer your filter code will only be executed when your interface filters actually change. I haven't actually tested this code
import { BindingEngine, Disposable, bindable } from 'aurelia-framework';
export class MyPage
{
#bindable searchTerm: string;
filteredSearchResults: any[];
subscription: Disposable;
constructor(private bindingEngine: BindingEngine)
{
}
attached()
{
this.subscription = this.bindingEngine.collectionObserver(this.filteredCategories).subscribe(this.filtersChanged);
}
detached()
{
this.subscription.dispose();
}
searchTermChanged(newValue: string, oldValue: string)
{
this.find();
}
find()
{
this.server.get('objects/?searchTerm=' + this.searchTerm)
.then(groupObjects=>
{
this.searchResults = groupObjects;
this.update();
});
}
update()
{
// your original filtering logic unchanged
var filteredGroups = this.searchResults.map(res =>
{
let filteredGroup = new Group;
filteredGroup = res.id,
filteredGroup = res.name;
filteredGroup = res.objects.filter(o => this.filteredCategories.some(fc => o.categories.some(oc => oc == fc)));
return filteredGroup;
});
filteredGroups = filteredGroups.filter(fg => fg.objects.length > 0);
// set value instead of using a computed property
this.filteredSearchResults = filteredGroups;
}
filtersChanged(splices)
{
this.update(); // you could remove this method and bind the array observable directly to update()
}
}

Categories

Resources