update an object with useState hook in React - reactjs

I have an object in board variable
Initial Data:
const [board, setBoard] = useState({
lanes: [],
});
{
lanes: [{
title: 'Bugs',
id: 'd83706b0-b252-11ec-8845-ad6b1e4ecd03',
cards: [{
id: null,
title: 'Bug #1',
description: 'Can AI make memes',
}, ],
},
{
title: 'Tests',
id: 'd83706b0-b252-11ec-8845-ad6b1e4ecd04',
cards: [{
id: null,
title: 'Test #1',
description: 'Can AI make memes',
}, ],
},
],
};
I want to add a new element to the cards array but only to the first element in the lanes array. Other answers seem to point to having to use a callback pattern, but I am quite unfamiliar with this.
Thanks for any help.

As for any modification you want to do on a useState variable, you must use an arrow function inside of the "set" function.
You can do something like that :
setBoard((currentBoard)=> {
currentBoard.lanes[0].cards = [...currentBoard.lanes[0].cards, whateverCardYouWantToAdd ]
return {... currentBoard} //necessary to create a new object because else the hook won't be updated
})

Maybe with this.
const addValue = () => {
let t = [...board];
t[0].lanes.cards.push({
id: null,
title: "Bug #1",
description: "Can AI make memes"
});
};

Related

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.

How to set state in nested array of objects in 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),
}
}
})

Why does forEach loop only set the last value if finds to state. ReactJS

const CategoriesData = [
{
name: "Category1",
isActive: true,
children: [
{
name: "Category1Child",
isActive: false,
}
]
},
{
name: "Category2",
isActive: false,
},
{
name: "Category3",
isActive: true,
children: [
{
name: "Category3Child",
isActive: false,
}
]
}
];
const [disabledCategories, setDisabledCategories] = useState([]);
function notActiveCategories(categories) {
// Loop logs out at least 7 isActive: false categories.
categories.forEach((category) => {
if (category.isActive) notActiveCategories(category.children);
if (!category.isActive) {
setDisabledCategories([...disabledCategories, category]);
console.log(category);
}
});
};
useEffect(() => {
notActiveCategories(CategoriesData);
console.log(disabledCategories); // Only 1 category is in the array.
}, []);
I feel like the function the loop is in calling itself is causing the disabledCategories state to revert to when it was empty and that is leading to only the last step of the foreach to be set.
So how would i get this to loop through the categories array and have the disabledCategories state to contain all of the category objects that have isActive: false.
Which in the example of CategoriesData above, it would mean that the disabledCategories state would contain:
[
{
name: "Category1Child",
isActive: false,
},
{
name: "Category2",
isActive: false,
},
{
name: "Category3Child",
isActive: false,
},
];
Try changing your setDisabledCategories to use the previous state param that comes from setState:
setDisabledCategories(prevState => [...prevState, category])
When multiple setState calls are batched together you need to be careful so they don't override each other. Using this method ensures that your setState calls are "chained" so you always get the updated state.
Way 1: Affect after recursive loop
function notActiveCategoriesRecusive(categories) {
let notActive = []
categories.forEach((category) => {
if (category.isActive) notActive = [...notActive, ...(notActiveCategories(category.children))];
if (!category.isActive) {
notActive.push(category)
}
});
return notActive
};
function notActiveCategories(categories) {
setDisabledCategories(notActiveCategoriesRecusive(categories)
}
Way 2: Get the last state because it doesn't has time to refresh
function notActiveCategories(categories) {
categories.forEach((category) => {
if (category.isActive) notActiveCategories(category.children);
if (!category.isActive) {
setDisabledCategories(oldState => ([...oldState, category]))
}
});
};
I'd only call setState once with the filtered array:
const findInactive = data =>
data.filter(e => !e.isActive)
.concat(...data.filter(e => e.children)
.map(e => findInactive(e.children)))
;
const categoriesData = [ { name: "Category1", isActive: true, children: [ { name: "Category1Child", isActive: false, } ] }, { name: "Category2", isActive: false, }, { name: "Category3", isActive: true, children: [ { name: "Category3Child", isActive: false, } ] } ];
const inactive = findInactive(categoriesData)
// the following is neeeded if it's possible for a
// node to have children and be inactive
// .map(({name, isActive}) => ({name, isActive}))
;
console.log(inactive);
//setDisabledCategories(inactive); // one time in React
This makes the code a lot easier to reason about and decouples React's API out from the filtering logic, which can be moved out to a generic function agnostic of React.
As others have mentioned, if you do want to call setState multiple times as a batch update, you can use the prevState callback to chain the updates: setDisabledCategories(prevState => [...prevState, category]);.

Filter out items in an array where a specified element is a null string

I'm very new to JS, and what I'm trying to do is create a new array that filters out elements in an existing array that have a null value. In my example code below, I would want to create a new array that filters out the third item because the url is an empty string (I only want it to filter based on whether the url is an empty string). I should add that the const is being exported as part of a reducer, and in the file it's actually used in, we're calling on {props.categories}. Any help is greatly appreciated!!!
const categories = () => {
return [
{
id: 0,
title: "Google",
url: "www.google.com",
},
{
id: 1,
title: "Firefox",
url: "www.firefox.com",
},
{
id: 2,
title: "Placeholder",
url: "",
},
];
};
Arrays have a filter function that takes a function to return true/false if the current element should be filtered through to the result array.
const categories = [
{
id: 0,
title: "Google",
url: "www.google.com",
},
{
id: 1,
title: "Firefox",
url: "www.firefox.com",
},
{
id: 2,
title: "Placeholder",
url: "",
},
];
const filterBlankUrl = arr => arr.filter(({ url }) => !!url);
const filteredCats = filterBlankUrl(categories);
console.log(filteredCats);
Thanks so much everyone for your help! I did actually finally figure it out; in the file where I was actually referencing the array, I needed to update the code to this:
options={props.categories.filter(categories => categories.url !== "")}

Filter Array based on a property in the array of its objects

Given is following data structure
const list = [
{
title: 'Section One',
data: [
{
title: 'Ay',
},
{
title: 'Bx',
},
{
title: 'By',
},
{
title: 'Cx',
},
],
},
{
title: 'Section Two',
data: [
{
title: 'Ay',
},
{
title: 'Bx',
},
{
title: 'By',
},
{
title: 'Cx',
},
],
},
];
What i want to do ist to filter this list based on title property in the data array of each object.
An example would be to have the list where the title property of the childs starts with "B", so the list will look like that:
const filteredList = [
{
title: 'Section One',
data: [
{
title: 'Bx',
},
{
title: 'By',
}
],
},
{
title: 'Section Two',
data: [
{
title: 'Bx',
},
{
title: 'By',
}
],
},
];
What i tried so far was something like that:
const items = list.filter(item =>
item.data.find(x => x.title.startsWith('A')),
);
or
const filtered = list.filter(childList => {
childList.data.filter(item => {
if (item.title.startsWith('B')) {
return item;
}
return childList;
});
});
But i think i am missing a major point here, maybe some of you could give me a tip or hint what i am doing wrong
Best regards
Your issue is that you're doing .filter() on list. This will either keep or remove your objects in list. However, in your case, you want to keep all objects in list and instead map them to a new object. To do this you can use .map(). This way you can map your objects in your list array to new objects which contain filtered data arrays. Here's an example of how you might do it:
const list=[{title:"Section One",data:[{title:"Ay"},{title:"Bx"},{title:"By"},{title:"Cx"}]},{title:"Section Two",data:[{title:"Ay"},{title:"Bx"},{title:"By"},{title:"Cx"}]}];
const filterByTitle = (search, arr) =>
arr.map(
({data, ...rest}) => ({
...rest,
data: data.filter(({title}) => title.startsWith(search))
})
);
console.log(filterByTitle('B', list));

Resources