How to set state in nested array of objects in ReactJs? - reactjs

I have this object as a state in reactjs. I want to add another object inside the "childoptions: []" array which is nested inside the options array on button click.
How can I achieve this, pls help...
const [select1, setSelect1] = useState({
id: uuid(),
type: 'select',
properties: {
label: 'Select1',
options: [
// {
// id: uuid(),
// optionName: 'red 🔴',
// value: '',
// childOptions: [],
// },
// {
// id: uuid(),
// optionName: 'green 🟢',
// value: '',
// childOptions: [],
// },
// {
// id: uuid(),
// optionName: 'blue 🔵',
// value: '',
// childOptions: [],
// },
],
},
parentId: null,
});

This is achievable by copy the prevState in the new state with the new object inserted inside the options array.
A more detailed explanation could be found at https://stackoverflow.com/a/26254086/9095807
setSelect1((prevState) => {
return {
...prevState,
properties: {
label: 'Select1',
options: prevState.properties.options.map(obj => obj.id === id ? { ...obj, childOptions: [...obj.childOptions, newChildOptions] } : obj),
}
}
})

Related

How to update nested state in redux

I'm a bit stuck with redux. I want to create reducer that can update state onClick with data that provided in each button.
Here's my TabSlice.ts
interface ITabContext {
tabIndex: number,
posterUrlFetch: string,
tabData: {
fetchUrl: string;
title: string;
}[]
}
const initialState = {
tabIndex: 0,
posterUrlFetch: 'movie/popular',
tabData: [
{ fetchUrl: 'movie/popular', title: 'Trending' },
{ fetchUrl: 'movie/upcoming', title: 'Upcoming' },
{ fetchUrl: 'tv/popular', title: 'TV Series' },
]
}
const tabSlice = createSlice({
name: 'tab',
initialState: initialState as ITabContext,
reducers: {
changeTab(state, action: PayloadAction<ITab>) {
const newItem = action.payload;
return state = {
tabIndex: newItem.tabIndex,
posterUrlFetch: newItem.posterUrlFetch,
tabData: [
{ fetchUrl: newItem.posterUrlFetch, title: newItem.posterUrlFetch },
]
}
}
}
})
Then I dispatch changeTab in my component and create function onClick:
const click = () => dispatch(changeTab({
tabIndex: 1,
posterUrlFetch: 'movie/popular',
tabData: [
{
fetchUrl: 'tv/latest',
title: 'TV Latest'
},
{
fetchUrl: 'movie/popular',
title: 'Popular'
},
{
fetchUrl: 'movie/latest',
title: 'Latest'
},
]
}));
As i click some info updates, but in tabData I have only first object. How to make it to push all data to tabData, not only first one? Thanks!
Remove return state = {} from your reducer function and instead return the object as a whole.
return {
tabIndex: newItem.tabIndex,
posterUrlFetch: newItem.posterUrlFetch,
tabData: newItem.tabData,
};
For the payload's tabData you can pass newItem.tabData

Select an array inside an array in React

I'm trying to select an arrayfield inside an array. Following code is inserted in my useStore:
const useStore = create(
persist(set => ({
projectszustand: [
{
id: nanoid(),
name: 'Projekt Final 1',
notes: 'Hier sind ein paar Notizen',
begin: '01/01/2001',
end: '02/01/2001',
icon: 'https://www.skopos.de/wp-content/uploads/2021/04/Element-5.svg',
color: 'blue',
edit: false,
selected: true,
feature: [
{
id: nanoid(),
name: 'Feature Final 1',
begin: '01/01/2001',
end: '02/01/2001',
isChecked: false,
edit: false,
selected: false,
todo: [
{...and so on
So, I'm trying to go with forEach and set all selected fields in the feature array to false:
selectFeature: index => {
set(
produce(draft => {
draft.projectszustand[index].feature.forEach(element => {
element.selected = false;
});
draft.projectszustand[index].feature[index].selected =
!draft.projectszustand[index].feature[index].selected;
})
);
},
This no works. Error message is: TypeError: can't access property "feature", draft.projectszustand[index] is undefined
Has somebody an easy solution for this?
Thanks very much for helping.

Typescript - Check values in one array are present in another

I have the following array. I am using this array to dynamically produce checkboxes on my UI. This is being used to save user config as to what they will be able to see in a nav menu.
accessLevels: any = [
{
description: "Properties",
type: '1',
selected: false
},
{
description: "Equipment",
type: '2',
selected: false
},
{
description: "Jobs",
type: '3',
selected: false
},
{
description: "Calender",
type: '4',
selected: false
}
]
I am making a call to an API which returns me an array of the users config. So what I will get is an array of the pages and their type like this:
{
description: "Equipment",
type: '2'
},
{
description: "Jobs",
type: '3'
}
In the array returned from the API I am just getting the values that should appear checked on the check boxes so what I want to do is loop through the returned array and check if any of the types match any types in the checkbox array if they do I want to set 'selected' to true. Thus checking the checkbox.
Here is what I have so far:
async loadLandlordConfig(key: string) {
const result = await this.userService.loadLandlordConfig(key);
//let accessLevels = [];
this.selectedApiValues = result[0].accessLevels;
this.selectedApiValues.forEach((selectedValue)=> {
});
}
Im not sure how to cross check the values and then change selected to true.
Hope I have made everything clear enough.
Any questions please ask. All help appreciated.
const accessLevels: any[] = [
{
description: 'Properties',
type: '1',
selected: false
},
{
description: 'Equipment',
type: '2',
selected: false
},
{
description: 'Jobs',
type: '3',
selected: false
},
{
description: 'Calender',
type: '4',
selected: false
}];
const results: any[] = [
{
description: 'Equipment',
type: '2'
},
{
description: 'Jobs',
type: '3'
}];
accessLevels.forEach(accessLevel => {
accessLevel.selected = results.some(x => x.type === accessLevel.type); });
For small arrays
You can check for the existence within a filter using some:
const intersect = this.array1.filter(a1 =>
this.array2.some(a2 => a1.type === a2.type));
The problem with this is that you are doing multiple loops through array 2.
For larger arrays
To keep your loops to a constant number, you could create a map of one of your arrays, and check that within a filter of the other array:
const map2 = new Map(this.array2.map(x => [x.type, s]));
const intersect = this.array1.filter(a1 =>
map.has(a1.type));
This adds a little bit of complexity, but is more efficient for all but the simplest cases.
You can achieve what you want with the following simple example :
let accessLevels = [
{
description: "Properties",
type: '1',
selected: false
},
{
description: "Equipment",
type: '2',
selected: false
},
{
description: "Jobs",
type: '3',
selected: false
},
{
description: "Calender",
value: '4',
selected: false
}
]
let api = [
{
description: "Equipment",
type: '2'
},
{
description: "Jobs",
value: '3'
}
];
for(var i = 0; i < accessLevels.length; i++) {
accessLevels[i].selected = api.find(e => e.description === accessLevels[i].description) ? true : false;
}
console.log(accessLevels);
You can use Map collection to have O(1) while mapping the second array:
const unique = new Map(received.map(s => [s.description, s]));
const result = accessLevels.map(({ description, type})=>
({selected: (unique.get(description) ? true : false), type, description}))
An example:
let accessLevels = [
{
description: "Properties",
type: '1',
selected: false
},
{
description: "Equipment",
type: '2',
selected: false
},
{
description: "Jobs",
type: '3',
selected: false
},
{
description: "Calender",
type: '4',
selected: false
}
]
const received = [
{
description: "Equipment",
type: '2'
},
{
description: "Jobs",
value: '3'
}
];
const unique = new Map(received.map(s => [s.description, s]));
const result = accessLevels.map(({ description, type})=>
({selected: (unique.get(description) ? true : false), type, description}))
console.log(result);
I've made some changes to your code. Here's how it looks like:
async loadLandlordConfig(key: string) {
const result = await this.userService.loadLandlordConfig(key);
// assuming there's always a result.
// ACCESS_LEVELS is the list of your config.
accessLevels = result[0].accessLevels;
this.selectedApiValues = ACCESS_LEVELS.map((al: any) => {
const selected = accessLevels.findIndex(a => a.type === al.type) > -1;
return { ...al, selected }
})
}
This will give you with the value
this.selectedApiValues = [
{
description: "Properties",
type: '1',
selected: false
},
{
description: "Equipment",
type: '2',
selected: true
},
{
description: "Jobs",
type: '3',
selected: true
},
{
description: "Calender",
type: '4',
selected: false
}
]

Update state using nested map es6

My state has an array of objects that also contains an array of objects.
var state = {
prop1: null,
categories: [
{
categoryId: 1,
tags: [
{
tagId: 1,
name: 'AA11',
status: true,
},
{
tagId: 2,
name: 'AA22',
status: false,
}
]
},
{
categoryId: 2,
tags: [
{
tagId: 1,
name: 'BB11',
status: true,
},
{
tagId: 2,
name: 'BB22',
status: false, // let's say i want to toggle this
}
]
},
]
};
I have an action that will toggle a status of a tag. This action will receive parameters categoryId and tagId.
So far I've come up with this but it doesn't work
return {
...state,
categories: state.categories.map((category) => {
category.tags.map(tag => (
(tag.tagId === action.tagId && category.categoryId === action.categoryId) ? {
...tag,
status: !tag.status,
} : tag));
return category;
}),
};
I finally fixed the map code.
return {
...state,
categories: state.categories.map(category => ((category.id === action.categoryId) ?
{
...category,
tags: category.tags.map(tag => (tag.id === action.tagId ? {
...tag, status: !tag.status,
} : tag)),
} : category)),
};

How to merge/add variable in angular

Below is my user object. Once i submit form i am getting values for it along with priPhone , mobilePhone.
this.user = {
isActive: 1,
contactDetails: {
name: { }
},
};
}
mobilePhone:any={phoneNumber: '',type:'Mobile'};
primaryPhone:any={phoneNumber: '',type:'Primary'};
I have to set mobilePhone, primaryPhone details to User Object.
So that i want final object like this.
this.user = {
isActive: 1,
contactDetails: {
name: { }
},
phoneNumbers: [{
phoneNumber: '',
type: 'Primary'
}, {
phoneNumber: '',
type: 'Mobile'
}]
};
How to do it ?
This should work in javascript.
this.user.phoneNumbers = [];
this.user.phoneNumbers.push(mobilePhone);
this.user.phoneNumbers.push(primaryPhone);
or simply
this.user.phoneNumbers = [mobilePhone, primaryPhone];

Resources