How to update react JS state functional component - javascript

How can I change a specific object in a state for example, I have data like this
const colors= [
{
id: 1,
name: "red",
},
{
id: 1,
name: "green",
},
{
id: 2,
name: "blue",
},
];
const storage = [{
id:2,
name:"64 GB"
},
{
id:2,
name:"128 GB"
]
The state value should be like this [{id:1, name:'red"}, {id:2,name:"128GB"}]
Is there any way to update the state based on the id only? For example: I need to loop on the data and if the state.id === id it will update that specific object.
So in our example, I need to update the state to be [{id:1, name:'green"}, {id:2,name:"128GB"}] if the state.id === 1
Thanks in advance

You could always copy the current value in local var, modify and set again the state. Something like:
const [colors, setColor] = useState([
{
id: 1,
name: "red",
},
{
id: 1,
name: "green",
},
{
id: 2,
name: "blue",
},
]);
...
let colorCopy = Object.assign({}, colors); //<-- make a copy of color
colorCopy = colorCopy.map(x => {
if (x.id === 1) x.name = "black";
return x;
}; // modify name property for id === 1
setColor(colorCopy); //<-- set new state
EDIT
#Nicolas Menettrier and #andy mccullough raised a good point of discussion: "Why call Object.assign if map function already does a copy of colors array?" This is correct so you could also write above code in this way:
let colorCopy = colors.map(x => {
if (x.id === 1) x.name = "black";
return x;
}; // modify name property for id === 1
setColor(colorCopy); //<-- set new state
1 line code less.

You can update your array state of objects with a shallow copy and change the value, same as bellow:
import * as React from "react";
export default function App() {
const [colors, setColors] = React.useState([
{
id: 1,
name: "red"
},
{
id: 1,
name: "green"
},
{
id: 2,
name: "blue"
}
]);
const updateState = (id, name) => {
let copy = [...colors];
let color = copy.filter((x) => x.id == id)[0];
if (!color) return;
let index = copy.indexOf(color);
color.name = name;
copy[index] = color;
setColors(copy);
};
return (
<div className="App">
{colors.map((color, index) => (
<>
<p>
{color.id} : {color.name}
</p>
</>
))}
<button
onClick={() => {
updateState(2, "yellow");
}}
>
update
</button>
</div>
);
}

Related

How to remove item form array?

I have the following array in my React naitve app.
const [items, setItems] = useState([
{ answers: [{ id: 1 }, { id: 2 }] },
{ answers: [{ id: 1 }] },
{ answers: [{ id: 1 },] },
]);
I want to delete item from first row with id 2 So the final should look like that
[
{ answers: [{ id: 1 }] },
{ answers: [{ id: 1 }] },
{ answers: [{ id: 1 }] },
]
How can i do that ?
I tried to start like this
const onDelete = useCallback((itemId) => {
var newItems = [...items];
newItems = newItems[0].answers.filter(....) //I don't know how to continue
setItems(newItems);
}, []);
Sorry for the question but I'm new to react-native and javascript!
Assuming that you want to change the first object and you want do dynamically filter the answers based on your itemId, the onDelete function would look like this:
const onDelete = useCallback((itemId) => {
setItems((prev) => [
{ answers: prev[0].answers.filter(({ id }) => id !== itemId) },
...prev.slice(1),
]);
}, []);
I've expanded #sm3sher answer to allow removing answer ids at any index.
const onDelete = useCallback((answerId=2,itemIndex=0)=>{
const newItems = [...items];
let currentItem = newItems[itemIndex]
currentItem.answers = currentItem.answers.filter(({id})=> id !== answerId)
newItems[itemIndex] = currentItem;
setItems(newItems);
},[]);
You should create a new object, whose answers property is the filtered original. The filter callback can be ({id}) => id != 2:
newItems[0] = { answers: newItems[0].answers.filter(({id}) => id != 2) };
newItems = newItems.map(item => { return item.answers.filter(answer => answer.id !== 2)})

Update state of an object within the array - React

I have a state and within that state there is a list with several objects. What I basically want to do is take an index from this array, in this case [0], and change its state. So, in short, I want to take {id: "1", value: "world1"}, and change the id to 'something'. I made a code but it didn't come out as I expected.
this.state = {
myState: [
{ id: "1", value:"world1" },
{ id: "2", value:"world2" },
{ id: "3", value:"world3" },
]
}
const result = this.setState(prevState => ({
myState: {
...prevState[0].id,
id: 'something'
}
}))
console.log(result)
Not sure why you need this, but the issue I'm seeing here is that myState is initially an array, and then you're passing it back as an object.
Try this:
const result = this.setState(prevState => {
prevState.myState[0].id = 'something';
return prevState;
})
const state = {
myState: [{
id: "1",
value: "world1"
},
{
id: "2",
value: "world2"
},
{
id: "3",
value: "world3"
},
]
}
const setState = (({
myState
}) => {
const newState = {
myState: [...myState]
}
const array = newState.myState
const targetIndex = array.findIndex(({
id
}) => id === '1')
const item = array[targetIndex]
array[targetIndex] = { ...item,
id: 'something'
}
return newState;
})
console.log(setState(state))
Use Array.prototype.map and object spread operator
this.setState(prevState => {
return {
myState: prevState.myState.map(
myStateItem => {
if(myStateItem.id === '1') {
return {
...myStateItem,
id: 'something'
}
}
return {
...myStateItem
}
})
};
});

How to display items in React using array.map, useState, useEffect at random with onClick?

I'm trying to make a name generator component that will display a name with its corresponding meaning from an array when the user clicks a button. I'm using useState to set the initial state as well as to cycle through the array randomly, as well as useEffect (although not sure if its necessary). I would prefer to use the array.map() method for randomizing. Component code below:
import React, { useState, useEffect } from "react";
const GeneratorNew = () => {
const startingName = [{ id: 0, value: "Update Name!" }];
const nameList = [
{ id: 1, name: 'Astghik', meaning: 'little star' },
{ id: 2, name: 'Tagouhi', meaning: 'Queen' },
{ id: 3, name: 'Lusine', meaning: 'Moon' }]
;
const [stateOptions, setStateValues] = useState(startingName);
// startingName.push(...nameList);
useEffect(() => {
setStateValues(nameList);
}, []);
return (
<div>
<h1>{nameList.name}</h1>
<p>{nameList.meaning}</p>
<button>
{stateOptions.map((nameList, index) => (
<option key={nameList.id}>{nameList.value}</option>
))}
</button>
</div>
);
};
export default GeneratorNew;
import React, { useState, useEffect } from "react";
const GeneratorNew = () => {
const startingName = [{ id: 0, value: "Update Name!" }];
const nameList = [
{ id: 1, name: 'Astghik', meaning: 'little star' },
{ id: 2, name: 'Tagouhi', meaning: 'Queen' },
{ id: 3, name: 'Lusine', meaning: 'Moon' }]
;
const [stateOptions, setStateValues] = useState(startingName);
const handleOnClick = () => {
setStateValues(nameList[Math.floor(Math.random() * (nameList.length - 1))])
}
return (
<div>
<h1>{nameList.name}</h1>
<p>{nameList.meaning}</p>
<button onClick={() => handleOnClick()}>
{stateOptions.map((nameList, index) => (
<option key={nameList.id}>{nameList.value}</option>
))}
</button>
</div>
);
};
export default GeneratorNew;
No need to useEffect here and useState only having the index is enough.
Try something like this snippet. Generate the random index on each button click.
const GeneratorNew = () => {
const startingName = [{ id: 0, meaning: "Update Name!" }];
const nameList = [
{ id: 1, name: 'Astghik', meaning: 'little star' },
{ id: 2, name: 'Tagouhi', meaning: 'Queen' },
{ id: 3, name: 'Lusine', meaning: 'Moon' }]
;
const [randomIndex, setRandomIndex] = React.useState(-1);
const data = randomIndex !== -1 ? nameList[randomIndex] : startingName[0];
return (
<div>
<h1>{data.name}</h1>
<p>{data.meaning}</p>
<button onClick={() => setRandomIndex(Math.floor(Math.random() * nameList.length))}>
Get Random
</button>
</div>
);
};
ReactDOM.render(<GeneratorNew /> , document.querySelector('#app'))
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="app"> </div>

How to remove child by id and retrive the parent objects using filter

I am trying to filter the parent, by removing it's child id only by not matching. in case if there is no child exist, the parent should be removed.
I try like this, but not works.
var rm = 7;
var objects = [
{
name: "parent1",
id: 1,
blog: [
{
name: "child1",
id: 1
},
{
name: "child2",
id: 2
}
]
},
{
name: "parent2",
id: 2,
blog: [
{
name: "child3",
id: 3
},
{
name: "child4",
id: 4
}
]
},
{
name: "parent3",
id: 3,
blog: [
{
name: "child5",
id: 5
},
{
name: "child6",
id: 6
}
]
},
{
name: "parent4",
id: 3,
blog: [
{
name: "child6",
id: 7
}
]
},
]
var result = objects.filter(value => {
if(!value.blog) return;
return value.blog.some(blog => blog.id !== rm)
})
console.log(result);
What is wrong here, or some one show me the correct approach?
looking for :
need to remove the blog if the id is same as rm, parent with other children required to exist.
need to remove the parent, after remove the children, in case there is no child(blog) exist.
Live Demo
Loop through the list of parents, and inside that loop, try to remove blogs with the given id first. Once you have done that, you can check if the blogs property has became empty, and if so, filter it out:
// We're going to filter out objects with no blogs
var result = objects.filter(value => {
// First filter blogs that match the given id
value.blog = value.blog.filter(blog => blog.id !== rm);
// Then, if the new length is different than 0, keep the parent
return value.blog.length;
})
I think the below code is what you are looking for
var result = objects.map(value => {
const blog = value.blog.filter(blog => blog.id !== rm);
if(blog.length === 0) {
return;
}
value.blog = blog;
return value;
}).filter(item => item);
Demo: https://jsfiddle.net/7Lp82z4k/3/
var result = objects.map(parent => {
parent.blog = parent.blog.filter(child => child.id !== rm);
return parent}).filter(parent => parent.blog && parent.blog.length > 0);

Update state in React: array element nested in Object

I'm trying to build onChange search feature. In suggestion list, my state like this:
results: {
page: 1,
results: [
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'new value', label: 'new label'},
{value: 'vote_average.desc', label: 'Rating Descending'}
]
}
When the user picks {value: 'new value', label: 'new label'} object.
How can I shift object to the first index of results array of state?
For example: After Object was picked. The state should be like this:
results: {
page: 1,
results: [
{value: 'new value', label: 'new label'},
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'vote_average.desc', label: 'Rating Descending'}
]
}
My idea is using spread operator and filter but I don't know how to implement that.
Select Item method:
onSuggestionSelected = (event, {suggestion, suggestionValue }) => {
console.log('Selected', suggestion); // {value: 'new value', label: 'new label'}
if (suggestion.media_type === 'movie') {
this.navigate('/search/movie', suggestionValue, suggestion);
} else if (suggestion.media_type === 'tv') {
this.navigate('/search/tv', suggestionValue, suggestion);
} else {
this.navigate('/search', suggestionValue, suggestion);
}
};
After selected, it will navigate:
navigate = (pathname, queryValue, resultValue) => {
// ResultValue is that Object that I want to shift first.
this.props.history.push({
pathname,
search: `?query=${queryValue}`,
state: {results: this.state.data}});
};
//filtered the remaining item
let remainValue = result.filter((obj, key) => obj.value != suggestion.value);
//merge here
let newResult = [suggestion, ...remainValue]; //it will contain reordered item
At first we need to find an index using findIndex, then filter current state to exclude the object containing selected data.
Last thing we need to do is to combine results into one array using spread operator ....
Example:
const currentState = [
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'new value', label: 'new label'},
{value: 'vote_average.desc', label: 'Rating Descending'}
];
const selectedValue = {value: 'new value', label: 'new label'};
const onSelectionChange = (selected) => {
const selectedIndex = currentState.findIndex(({ value }) => value === selected.value);
const selectedItem = currentState[selectedIndex];
const stateExcludedItem = currentState.filter(({ value }) => value !== selected.value);
const newState = [ selectedItem, ...stateExcludedItem ]
// this.setState
return newState;
}
const result = onSelectionChange(selectedValue);
console.log(result);
You could find the index of the result you want to put first in the array, and then put that first followed by what used to be before and after the result in the array.
Example
class App extends React.Component {
state = {
results: {
page: 1,
results: [
{ value: "popularity.desc", label: "Popularity Descending" },
{ value: "popularity.asc", label: "Popularity Ascending" },
{ value: "new value", label: "new label" },
{ value: "vote_average.desc", label: "Rating Descending" }
]
}
};
putResultFirst = result => {
this.setState(previousState => {
const { results } = previousState.results;
const resultIndex = results.indexOf(result);
return {
results: {
...results,
results: [
result,
...results.slice(0, resultIndex),
...results.slice(resultIndex + 1)
]
}
};
});
};
render() {
return (
<div>
{this.state.results.results.map(result => (
<div key={result.id} onClick={() => this.putResultFirst(result)}>
{result.value} {result.label}
</div>
))}
</div>
);
}
}
To move the selected item to the first position, you can use the Array.filter method to get the unselected items as an array, and build a new results array:
selectedItem = this.state.results.results[clickedIndex]
unselectedItems = this.state.results.results.filter((item, index) => clickedIndex !== index)
Because the "results" in state is an object. To update an object in the state, you should make an copy of the current results object to create a new object, so that the state will be updated.
results = {...this.state.results, results: [selectedItem, ...unselectedItems]}
You just want to move the selected item to the top of the results array, therefore you need just transfer the index of selected item to the event handler, which will call the setState method.
The example codes:
class App extends React.Component {
state = {
results: {
page: 1,
results: [
{value: 'popularity.desc', label: 'Popularity Descending'},
{value: 'popularity.asc', label: 'Popularity Ascending'},
{value: 'new value', label: 'new label'},
{value: 'vote_average.desc', label: 'Rating Descending'}
]
}
};
handelClick = (clickedIndex) => {
if(clickedIndex === 0) {
return;
}
let selectedItem = this.state.results.results[clickedIndex],
unselectedItems = this.state.results.results.filter((item, index) => clickedIndex !== index),
results = {...this.state.results, results: [selectedItem, ...unselectedItems]};
this.setState({results})
};
render() {
return <ul>
{this.state.results.results.map((item, index) =>
<li key={item.label} onClick={this.handelClick.bind('', index)}>{item.label}</li>)
}
</ul>
}
}
const renderFilterList = () => {
ReactDOM.render(<App />, document.getElementById('react-app'));
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="react-app"></div>

Categories