I am new to React. In my state, I am trying to have an empty array initialized to store polygons.
The structure I want to have is an array like this.state.polyList = [[dot1,dot2,dot3...],[dot1,dot2,dot3...]]
I am trying to have a
let newState = Object.assign({},this.state);
let newValue = [points]; // "points" is [dot1,dot2,dot3...]
console.log(newValue[0][0]); // able to print correctly
newState.polyList = newState.polyList.concat(newValue)
this.setState(newState)
However, when I later log the state.polyList, I only have a few empty array ([]) inside list
You can add like this array to state array
state = {
items: [4,5,6],
};
function to add
handleClick = e => {
this.setState({
items:[...this.state.items,[Math.floor(Math.random()*100),7,8]]
})
};
Change to something like below.
let newValue = [points]; // "points" is [dot1,dot2,dot3...]
console.log(newValue[0][0]); // able to print correctly
let newState = { ...this.state,
polyList: [...this.state.polyList, newValue] };
this.setState(newState)
Deep cloning in react doesn't work the way you are doing. A way that i prefer to use is by spread operator. You can do something like:
let newState = [...this.state.polyList];
newState.push(points)
this.setState({ polyList: newState });
Hope this works for you.
Best way to do this would be is structuring.
let oldState = [1,2,4]
this.setState ([...oldState, [new state array]])
Related
The story is, I should be able to put Bob, Sally and Jack into a box. I can also remove either from the box. When removed, no slot is left.
people = ["Bob", "Sally", "Jack"]
I now need to remove, say, "Bob". The new array would be:
["Sally", "Jack"]
Here is my react component:
...
getInitialState: function() {
return{
people: [],
}
},
selectPeople(e){
this.setState({people: this.state.people.concat([e.target.value])})
},
removePeople(e){
var array = this.state.people;
var index = array.indexOf(e.target.value); // Let's say it's Bob.
delete array[index];
},
...
Here I show you a minimal code as there is more to it (onClick etc). The key part is to delete, remove, destroy "Bob" from the array but removePeople() is not working when called. Any ideas? I was looking at this but I might be doing something wrong since I'm using React.
When using React, you should never mutate the state directly. If an object (or Array, which is an object too) is changed, you should create a new copy.
Others have suggested using Array.prototype.splice(), but that method mutates the Array, so it's better not to use splice() with React.
Easiest to use Array.prototype.filter() to create a new array:
removePeople(e) {
this.setState({people: this.state.people.filter(function(person) {
return person !== e.target.value
})});
}
To remove an element from an array, just do:
array.splice(index, 1);
In your case:
removePeople(e) {
var array = [...this.state.people]; // make a separate copy of the array
var index = array.indexOf(e.target.value)
if (index !== -1) {
array.splice(index, 1);
this.setState({people: array});
}
},
Here is a minor variation on Aleksandr Petrov's response using ES6
removePeople(e) {
let filteredArray = this.state.people.filter(item => item !== e.target.value)
this.setState({people: filteredArray});
}
Simple solution using slice without mutating the state
const [items, setItems] = useState(data);
const removeItem = (index) => {
setItems([
...items.slice(0, index),
...items.slice(index + 1)
]);
}
Use .splice to remove item from array. Using delete, indexes of the array will not be altered but the value of specific index will be undefined
The splice() method changes the content of an array by removing existing elements and/or adding new elements.
Syntax: array.splice(start, deleteCount[, item1[, item2[, ...]]])
var people = ["Bob", "Sally", "Jack"]
var toRemove = 'Bob';
var index = people.indexOf(toRemove);
if (index > -1) { //Make sure item is present in the array, without if condition, -n indexes will be considered from the end of the array.
people.splice(index, 1);
}
console.log(people);
Edit:
As pointed out by justin-grant, As a rule of thumb, Never mutate this.state directly, as calling setState() afterward may replace the mutation you made. Treat this.state as if it were immutable.
The alternative is, create copies of the objects in this.state and manipulate the copies, assigning them back using setState(). Array#map, Array#filter etc. could be used.
this.setState({people: this.state.people.filter(item => item !== e.target.value);});
Easy Way To Delete Item From state array in react:
when any data delete from database and update list without API calling that time you pass deleted id to this function and this function remove deleted recored from list
export default class PostList extends Component {
this.state = {
postList: [
{
id: 1,
name: 'All Items',
}, {
id: 2,
name: 'In Stock Items',
}
],
}
remove_post_on_list = (deletePostId) => {
this.setState({
postList: this.state.postList.filter(item => item.post_id != deletePostId)
})
}
}
filter method is the best way to modify the array without touching the state.
It returns a new array based on the condition.
In your case filter check the condition person.id !== id and create a new array excluding the item based on condition.
const [people, setPeople] = useState(data);
const handleRemove = (id) => {
const newPeople = people.filter((person) => person.id !== id);
setPeople( newPeople);
};
<button onClick={() => handleRemove(id)}>Remove</button>
Not advisable:
But you can also use an item index for the condition if you don't have any id.
index !== itemIndex
This is your current state variable:
const [animals, setAnimals] = useState(["dogs", "cats", ...])
Call this function and pass the item you would like to remove.
removeItem("dogs")
const removeItem = (item) => {
setAnimals((prevState) =>
prevState.filter((prevItem) => prevItem !== item)
);
};
your state variable now becomes:
["cats", ...]
Another way of doing it is by using useState hook. Check docs: https://reactjs.org/docs/hooks-reference.html#functional-updates It states: Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax as shown below or use useReducer hook.
const [state, setState] = useState({});
setState(prevState => {
return {...prevState, ...updatedValues};
});
Some answers mentioned using 'splice', which did as Chance Smith said mutated the array. I would suggest you to use the Method call 'slice'
(Document for 'slice' is here) which make a copy of the original array.
Just filter out deleted item and update the state with remaining items again,
let remainingItems = allItems.filter((item) => {return item.id !== item_id});
setItems(remainingItems);
const [people, setPeople] = useState(data);
const handleRemove = (id) => {
const newPeople = people.filter((person) => { person.id !== id;
setPeople( newPeople );
});
};
<button onClick={() => handleRemove(id)}>Remove</button>
It's Very Simple
First You Define a value
state = {
checked_Array: []
}
Now,
fun(index) {
var checked = this.state.checked_Array;
var values = checked.indexOf(index)
checked.splice(values, 1);
this.setState({checked_Array: checked});
console.log(this.state.checked_Array)
}
removePeople(e){
var array = this.state.people;
var index = array.indexOf(e.target.value); // Let's say it's Bob.
array.splice(index,1);
}
Redfer doc for more info
Almost all the answers here seem to be for class components, here's a code that worked for me in a functional component.
const [arr,setArr]=useState([]);
const removeElement=(id)=>{
var index = arr.indexOf(id)
if(index!==-1){
setArr(oldArray=>oldArray.splice(index, 1));
}
}
If you use:
const[myArr, setMyArr] = useState([]);
for add:
setMyArr([...myArr, value]);
and for remove:
let index = myArr.indexOf(value);
if(index !== -1)
setPatch([...myArr.slice(0, index), ...myArr.slice(index, myArr.length-1)]);
Removing an element with a certain value
//
Note filter function always returns a new array.
const people = ["Bob", "Sally", "Jack"]
const removeEntry = (remove) => {
const upDatePeople = people.filter((Person) =>{
return Person !== remove
});
console.log(upDatePeople)
//Output: [ 'Sally', 'Jack' ]
}
removeEntry("Bob");
You forgot to use setState. Example:
removePeople(e){
var array = this.state.people;
var index = array.indexOf(e.target.value); // Let's say it's Bob.
delete array[index];
this.setState({
people: array
})
},
But it's better to use filter because it does not mutate array.
Example:
removePeople(e){
var array = this.state.people.filter(function(item) {
return item !== e.target.value
});
this.setState({
people: array
})
},
const [randomNumbers, setRandomNumbers] = useState([111,432,321]);
const numberToBeDeleted = 432;
// Filter (preferred)
let newRandomNumbers = randomNumbers.filter(number => number !== numberToBeDeleted)
setRandomNumbers(newRandomNumbers);
//Splice (alternative)
let indexOfNumberToBeDeleted = randomNumbers.indexOf(numberToBeDeleted);
let newRandomNumbers = Array.from(randomNumbers);
newRandomNumbers.splice(indexOfNumberToBeDeleted, 1);
setRandomNumbers(newRandomNumbers);
//Slice (not preferred - code complexity)
let indexOfNumberToBeDeleted = randomNumbers.indexOf(numberToBeDeleted);
let deletedNumber = randomNumbers.slice(indexOfNumberToBeDeleted, indexOfNumberToBeDeleted+1);
let newRandomNumbers = [];
for(let number of randomNumbers) {
if(deletedNumber[0] !== number)
newRandomNumbers.push(number);
};
setRandomNumbers(newRandomNumbers);
The story is, I should be able to put Bob, Sally and Jack into a box. I can also remove either from the box. When removed, no slot is left.
people = ["Bob", "Sally", "Jack"]
I now need to remove, say, "Bob". The new array would be:
["Sally", "Jack"]
Here is my react component:
...
getInitialState: function() {
return{
people: [],
}
},
selectPeople(e){
this.setState({people: this.state.people.concat([e.target.value])})
},
removePeople(e){
var array = this.state.people;
var index = array.indexOf(e.target.value); // Let's say it's Bob.
delete array[index];
},
...
Here I show you a minimal code as there is more to it (onClick etc). The key part is to delete, remove, destroy "Bob" from the array but removePeople() is not working when called. Any ideas? I was looking at this but I might be doing something wrong since I'm using React.
When using React, you should never mutate the state directly. If an object (or Array, which is an object too) is changed, you should create a new copy.
Others have suggested using Array.prototype.splice(), but that method mutates the Array, so it's better not to use splice() with React.
Easiest to use Array.prototype.filter() to create a new array:
removePeople(e) {
this.setState({people: this.state.people.filter(function(person) {
return person !== e.target.value
})});
}
To remove an element from an array, just do:
array.splice(index, 1);
In your case:
removePeople(e) {
var array = [...this.state.people]; // make a separate copy of the array
var index = array.indexOf(e.target.value)
if (index !== -1) {
array.splice(index, 1);
this.setState({people: array});
}
},
Here is a minor variation on Aleksandr Petrov's response using ES6
removePeople(e) {
let filteredArray = this.state.people.filter(item => item !== e.target.value)
this.setState({people: filteredArray});
}
Simple solution using slice without mutating the state
const [items, setItems] = useState(data);
const removeItem = (index) => {
setItems([
...items.slice(0, index),
...items.slice(index + 1)
]);
}
Use .splice to remove item from array. Using delete, indexes of the array will not be altered but the value of specific index will be undefined
The splice() method changes the content of an array by removing existing elements and/or adding new elements.
Syntax: array.splice(start, deleteCount[, item1[, item2[, ...]]])
var people = ["Bob", "Sally", "Jack"]
var toRemove = 'Bob';
var index = people.indexOf(toRemove);
if (index > -1) { //Make sure item is present in the array, without if condition, -n indexes will be considered from the end of the array.
people.splice(index, 1);
}
console.log(people);
Edit:
As pointed out by justin-grant, As a rule of thumb, Never mutate this.state directly, as calling setState() afterward may replace the mutation you made. Treat this.state as if it were immutable.
The alternative is, create copies of the objects in this.state and manipulate the copies, assigning them back using setState(). Array#map, Array#filter etc. could be used.
this.setState({people: this.state.people.filter(item => item !== e.target.value);});
Easy Way To Delete Item From state array in react:
when any data delete from database and update list without API calling that time you pass deleted id to this function and this function remove deleted recored from list
export default class PostList extends Component {
this.state = {
postList: [
{
id: 1,
name: 'All Items',
}, {
id: 2,
name: 'In Stock Items',
}
],
}
remove_post_on_list = (deletePostId) => {
this.setState({
postList: this.state.postList.filter(item => item.post_id != deletePostId)
})
}
}
filter method is the best way to modify the array without touching the state.
It returns a new array based on the condition.
In your case filter check the condition person.id !== id and create a new array excluding the item based on condition.
const [people, setPeople] = useState(data);
const handleRemove = (id) => {
const newPeople = people.filter((person) => person.id !== id);
setPeople( newPeople);
};
<button onClick={() => handleRemove(id)}>Remove</button>
Not advisable:
But you can also use an item index for the condition if you don't have any id.
index !== itemIndex
This is your current state variable:
const [animals, setAnimals] = useState(["dogs", "cats", ...])
Call this function and pass the item you would like to remove.
removeItem("dogs")
const removeItem = (item) => {
setAnimals((prevState) =>
prevState.filter((prevItem) => prevItem !== item)
);
};
your state variable now becomes:
["cats", ...]
Another way of doing it is by using useState hook. Check docs: https://reactjs.org/docs/hooks-reference.html#functional-updates It states: Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax as shown below or use useReducer hook.
const [state, setState] = useState({});
setState(prevState => {
return {...prevState, ...updatedValues};
});
Some answers mentioned using 'splice', which did as Chance Smith said mutated the array. I would suggest you to use the Method call 'slice'
(Document for 'slice' is here) which make a copy of the original array.
Just filter out deleted item and update the state with remaining items again,
let remainingItems = allItems.filter((item) => {return item.id !== item_id});
setItems(remainingItems);
const [people, setPeople] = useState(data);
const handleRemove = (id) => {
const newPeople = people.filter((person) => { person.id !== id;
setPeople( newPeople );
});
};
<button onClick={() => handleRemove(id)}>Remove</button>
It's Very Simple
First You Define a value
state = {
checked_Array: []
}
Now,
fun(index) {
var checked = this.state.checked_Array;
var values = checked.indexOf(index)
checked.splice(values, 1);
this.setState({checked_Array: checked});
console.log(this.state.checked_Array)
}
removePeople(e){
var array = this.state.people;
var index = array.indexOf(e.target.value); // Let's say it's Bob.
array.splice(index,1);
}
Redfer doc for more info
Almost all the answers here seem to be for class components, here's a code that worked for me in a functional component.
const [arr,setArr]=useState([]);
const removeElement=(id)=>{
var index = arr.indexOf(id)
if(index!==-1){
setArr(oldArray=>oldArray.splice(index, 1));
}
}
If you use:
const[myArr, setMyArr] = useState([]);
for add:
setMyArr([...myArr, value]);
and for remove:
let index = myArr.indexOf(value);
if(index !== -1)
setPatch([...myArr.slice(0, index), ...myArr.slice(index, myArr.length-1)]);
Removing an element with a certain value
//
Note filter function always returns a new array.
const people = ["Bob", "Sally", "Jack"]
const removeEntry = (remove) => {
const upDatePeople = people.filter((Person) =>{
return Person !== remove
});
console.log(upDatePeople)
//Output: [ 'Sally', 'Jack' ]
}
removeEntry("Bob");
You forgot to use setState. Example:
removePeople(e){
var array = this.state.people;
var index = array.indexOf(e.target.value); // Let's say it's Bob.
delete array[index];
this.setState({
people: array
})
},
But it's better to use filter because it does not mutate array.
Example:
removePeople(e){
var array = this.state.people.filter(function(item) {
return item !== e.target.value
});
this.setState({
people: array
})
},
const [randomNumbers, setRandomNumbers] = useState([111,432,321]);
const numberToBeDeleted = 432;
// Filter (preferred)
let newRandomNumbers = randomNumbers.filter(number => number !== numberToBeDeleted)
setRandomNumbers(newRandomNumbers);
//Splice (alternative)
let indexOfNumberToBeDeleted = randomNumbers.indexOf(numberToBeDeleted);
let newRandomNumbers = Array.from(randomNumbers);
newRandomNumbers.splice(indexOfNumberToBeDeleted, 1);
setRandomNumbers(newRandomNumbers);
//Slice (not preferred - code complexity)
let indexOfNumberToBeDeleted = randomNumbers.indexOf(numberToBeDeleted);
let deletedNumber = randomNumbers.slice(indexOfNumberToBeDeleted, indexOfNumberToBeDeleted+1);
let newRandomNumbers = [];
for(let number of randomNumbers) {
if(deletedNumber[0] !== number)
newRandomNumbers.push(number);
};
setRandomNumbers(newRandomNumbers);
I have cloned an array and I have an onChange function. On calling onChange function my original and cloned array is updating according to the updated onChange value. But I don't want to update my cloned array. How can I do this ?
My code -
const clonedArray = [...originalArray];
const onChange = (id:number, value: string): void => {
const arrayData = originalArray;
const selectedData = arrayData.find(
(data) => data.myId === id
);
if (selectedData) {
// updating my originalArray according to new changed value;
}
}
this onChange function is also updating my clonedArray. I don't wanna update my clonedArray. How can I do this ? What is the solution useMemo or something ? Can anyone tell me the solution for it ?
Issue
This issue is state object/array mutation. You are mutating elements in an array and seeing the mutations manifest in a "copy".
Firstly, const clonedArray = [...originalArray]; is only a shallow copy of the originalArray, not a clone. This means that other than the array reference itself, all the elements in clonedArray still refer to the same elements in originalArray.
Secondly, if later you are making changes in originalArray and you are seeing them manifest in clonedArray then you are definitely mutating the element references instead of creating new references.
Solution
You are looking for the Immutable Update Pattern. When updating state in React it is necessary to shallow copy not only the root object/array that is being updated, but all nested state as well. To help with endeavor, especially when updating arrays, you will want to also use functional state updates so you can correctly update from the previous state.
For this I'm assuming (based on a comment that originalArray was in state) that your state looks something like this:
const [originalArray, setOriginalArray] = useState([]);
The change handler/state update (just an example since I don't know your exact update requirements)
const onChange = (id: number, value: string): void => {
const selectedData = originalArray.find((data) => data.myId === id);
if (selectedData) {
// updating my originalArray according to new changed value
// Array.prototype.map shallow copies the array into a new array reference
setOriginalArray(originalArray => originalArray.map(data => data.myId === id
? { // <-- updating element into new object reference
...data, // <-- shallow copy previous data element
property: value // <-- update property with new value
}
: data // <-- not updating, pass previous object through
);
}
}
Once you are correctly updating the array elements, then no mutations will occur to anything that may also be referencing the state.
you can use useMemo with empty dependency array. whenever originalArray updates, clonedArray doesn't change.
import { useMemo, useState } from "react";
const arr = [1,2,3]
export default function App() {
const [originalArray, setOriginalArray] = useState(arr)
const clonedArray = useMemo(() => originalArray, [])
return (
<div className="App" style={{textAlign:'center'}}>
originalArray: {originalArray}
<br/>
clonedArray:{clonedArray}
<br/>
<button onClick={() => setOriginalArray([1])}>update original</button>
</div>
);
}
I have a problem with a mutation I can't understand, here is my code :
const copyItems = Object.assign({}, state.items);
for(const typeItems of Object.values(copyItems[0])){
for(let items of typeItems){
if(items.id === action.item.id){
Object.assign(items, action.item);
}
}
}
return state.items;
I also tried by using const copyItems = state.items.slice().map(o => ({...o}) but even by returning the state the value is modified. Why?
Your state.items is a nested object. Then you can do these ways:
const newObj = JSON.parse(JSON.stringify(obj))
follow this link
or you can use lodash
const newObj = _.cloneDeep(obj)
Your copyItems copies the state.items that would have the same reference.
const copyItems = Object.assign({}, state.items);
Then you are mutating the state.items when you do the following.
Object.assign(items, action.item);
The answer from #bird may work but you will into problem with if you have circular structure or if the object has references to other objects then you will lose all those references. But it really depends on what you are trying to achieve.
Using Object.assign seems dangerous in terms of functional programming required by Redux. Should be fine if you use it on empty object (Object.assign({}, ...)) but not on an existing object.
I have an example code below that might help you case.
const state = {items:[{id:0,value:"default"},{id:1,value:"default"}]};
const action = {item:{id:0,value:"new"}};
const copyItems = state.items.map(o => o.id === action.item.id ? action.item : o);
console.log(state.items, copyItems);
I saw this in somewhere and I'm confused. I've never do thing this way, what it does actually?
doSomething = index => {
const tempState = this.state
tempState.keys.push(index)
this.setState({
...tempState
})
}
const tempState = this.state
The above assigns to tempState a reference to the state object
tempState.keys.push(index)
tempState has a property of keys that holds a reference to an array and push is called on the array to add index to the array
this.setState({
...tempState
})
this.setState is called and a new copy of the state is passed into setState.
The spread operator is basically copying the content of the old state into a new object.
The above implementation isn't the perfect one and should be improved so that you aren't mutating the original state object.
doSomething = index => {
const tempState = this.state // assing reference of state to tempState
tempState.keys.push(index) // push a value to `keys` property value within state. Note that Javascript object assignment works by reference and hence this will actually mutate the state object
this.setState({
...tempState
}) // call setState to update state and pass the cloned version of tempState using spread syntax as the new state values
}
Although the above implementation clones the value before setState, its incorrect since it should clone the value before any update is made into it
doSomething = index => {
const keys = [...this.state.keys]
keys.push(index)
this.setState({
keys
})
}
However since you use ES5, in order to clone the value, you can make use of concat to update the keys array using functional setState like
doSomething = index => {
this.setState(prevState => ({
keys: prevState.keys.concat([index])
}))
}
I hope the above answer provides an insight into the explanation that you were looking for