How to set/update clicked item in React.js? - javascript

Could you please tell me how to how to set/update a clicked item in React.js?.
I want the value of the clicked element to change to "test". How can I setup such event handler to do this?
Here is my code
On item click I am trying to update the item like that
btnClick(obj) {
obj.hse = 'test';
console.log(obj);
// this.setState({
// data: [obj]
// });
}

Since your data object is an array, I think the easiest way to implement this is to send your btnClick() function the id of the element that was clicked, update that value, and then save the new state.
Codepen
Like so:
this.state.data.map((item, i) => {
return <li onClick = {
this.btnClick.bind(this, i)
> {
item.hse
} < /li>;
})
By changing map(item) => { to map(item, i) => { you make use of the index parameter of the Array map method. This i variable is then used when binding the btnClick function.
btnClick(id) {
let temp = this.state.data.slice();
temp[id].hse = 'test';
this.setState({
data: temp
});
}
Here, the id is the index of the item clicked. Start off by creating a shallow copy of the this.state.data and put it into a local temp variable. Then, change the hse property of temp[id]. Finally, update the data state with the local variable.
edit: fixed broken codepen link

Related

React state update with slice does not work

I want to understand why the code can not work by copying the array from the state. checkboxes is an array of Booleans.
I was of the opinion that if I assign checkboxes from state directly to tempCheckboxes it will be an alias and modifying an alias will indirectly impact state variable. But that is not the case. And I want to know why? Thanks for your help.
checkRow = (index, checked) => {
let tempCheckboxes = this.state.checkboxes.slice();
let all = true;
tempCheckboxes[index] = checked;
all = tempCheckboxes.every((isChecked) => this.isTrue(isChecked));
this.setState((prevState) => ({ ...prevState, checkboxes: tempCheckboxes, checkAll: all }));
};
Above code fails to work but following code works
checkRow = (index, checked) => {
let tempCheckboxes = this.state.checkboxes
let all = true;
tempCheckboxes[index] = checked;
all = tempCheckboxes.every((isChecked) => this.isTrue(isChecked));
this.setState((prevState) => ({ ...prevState, checkboxes: tempCheckboxes, checkAll: all }));
};
Edit : Reason for this question is in react we should not directly change the state with assignment but instead use setState to do so. In my opinion 2nd version of code is doing it. Earlier title for the question was not precise so changed it.
So the question is about javascript object reference.
Slice does not alter the original array, while the direct = which you are doing is creating a reference to the original object. So what you change in tempCheckboxes, will directly affect this.state.checkboxes.
In your second option tempCheckboxes will be a reference to the original this.state.checkboxes
In the first option you will refer to a new object in memory.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice

Petite-Vue filtered array reactivity?

I played arround with Petite-Vue and really like it. Works fine with reactivity / ui updates and helpers like "$el" and "$refs" for easy form field handling / data binding.
Now I tried to have a reactive list with updates live if backend data (gundb) changes.
UI updates work fine if I list all array entries with "v-for".
If I try to filter the array of players by one team (object property "team") the UI is initialy correct rendered, but if a players name changed the update is pushed to gundb and by listener also to players array based on PetiteVue.reative(). So all is fine, but no UI update is triggered. Item is a Proxy as needed for (petite-)vue reactivity.
So it looks like a players.filter(...) call break reactivity. Also tried a method and getter inside of a component and also directly by write players.filter(...) to the html template.
Do I missing the right way how to trigger a re-render of changed filtered reactive array of objects?
I have a function to sync gundb "backend" changes to Petite-Vue.reactive object
gunArray2vue = (gunNode, vueArray) => {
gunNode.map().on((value, soul) => {
item = vueArray.find(item => item && item._ && item._['#'] === soul)
console.log('gunArray2vue', `soul=${soul}`, value, item)
if(!!item && value === null) {
// item in VUE but seems deleted... remove from vue array...
console.log("ToDeleteItem", item)
vueArray.splice(vueArray.indexOf(item), 1)
} else if(!value) {
console.log("Ignore creation of empty object")
return // don't add empty object...
} else if(item) {
//vueArray[vueArray.indexOf(item)] = value // update
console.log("UpdateItem")
item = value // update
} else {
console.log("AddArrayItem")
vueArray.push(value) // add new
}
})
}
Changed Value checked with console:
checked change in js console
But UI isn't refreshed.
Here the code in componente
tested as getter / function
//filteredPlayers: function() {
get filteredPlayers() {
if(this.form.nr || this.form.name) {
return this.players.filter(player =>
player.name.includes(this.form.name) || player.nr == this.form.nr
)
} else {
return this.players
}
}
The filtering is reactive to my input field! But changed name is ignored.
And here is a simpler getter function
get myPlayers() {
page.players
}
But ui isn't updated if an item changes without refresh.
So filtering works reactive, but changed items are not detected and re-rendered.
Also tried directly to use the unfiltered PetiteVue.reactive object like that.
<div v-for="player in page.players">{{player.nr}} {{player.name}}</div>
Rendering works fine, but also not updated on change.
Update
Noticed during my tests today, that updates work if I directly change my original object page.players, but not / no more if I use a reference to the original reactive object?
So I can't use the reactive object by reference without loose ui reactivity for item changes?
Update2
Add and remove an reactive array entry works fine!
Just update an item won't work. Tested different ways to set the item object / key -> val of object. Change isn't reactive.
Function with my tests
gun2arrayListener = (name, table) => {
console.log(`Register Listener Gun table "${name}"`)
DB.get(name).map().on((value, soul) => {
console.log("DEBUG", soul, value)
item = table.find(item => item?._['#'] === soul)
index = table.indexOf(item)
console.log('gun2array', `soul=${soul}`, value, item)
if(Object.is(item, value)) {
console.log("Skip unchanged item")
} else if(item && value === null) {
console.log("ToDeleteItem", item)
table.splice(index, 1)
} else if(!value) {
console.log("Ignore creation of empty object?!")
} else if(item) {
console.log("UPDATE", "ARRAY", item, value)
// !!! CHANGES NOT REACTIVE HERE !!!
// !!! method is triggered, changes looks good, but reload page needed to update UI !!!
//item = value
//TABLES[name][index] = value
//Object.assign(item, value)
//table.splice(index, 1, value)
//table.splice(index, 1); table.push(value)
for (const [key, val] of Object.entries(value)) {
console.log(`${key}: ${val}`)
//item[key] = val
store.players[index][key] = val
console.log(item)
}
} else {
console.log("AddNewGunNodeItem", value)
table.push(value) // add new
}
})
}
Strange... if I test reactivity from outside it works ?!
store.players[0].name = "BUM!" // works as needed in function... ?!
I noticed gun fires the listener twice, but that should result in correct updated array object, just changed to the new value twice...
I can't prevent that, because can't compare old object (vue proxy object) with the new object (plain object without vue proxy) to skip unchanged object here. Object.is results in false because of the Proxy arround...

State value goes empty to child component

I'm trying to send information from parent to child component with a state.
In the parent component, I'm getting the value and storing it inside an array named valueArray. Later on, this array will be stored in a state and sent through child component to map and display the values.
1-) Function below takes the values selected by user and pushes them inside an empty array valueArray.
let valueArray = [];
const getSelectedItem = (event) =>{
if(valueArray.includes(item)){
valueArray.splice(item.index,1);
}else{
valueArray.push(item);
}
}
2-) In here, after user done with selecting values, another function to store values in state will be initiated by onClick, this function uses valueArray as argument.
<button onClick={() => handleState(valueArray)}>Buton</button>
3-) After the button click, handleState function executes and updates state with the elements in valueArray, values array goes to child component to map and perform display.
const [values,setValues] = useState([]);
const handleState = (arr) =>{
setValues([...arr])
}
Major problem in here is, when button clicked at the first time after selection, state becomes empty array and child component can't execute mapping because valueArray's initial value is []. Second click works and state changes with values inside the valueArray.
Use State like this.
const [valueArray, setValueArray] = [];
const getSelectedItem = (event) =>{
const array = [...valueArray];
if(array.includes(item)){
array.splice(item.index, 1);
// setValueArray([...valueArray.filter((ite) => ite != item)]);
setValueArray([...array]);
}else{
// valueArray.push(item);
setValueArray([...array, item]); // instead of push we should use spread or concat()
}
}

React remove item from array state doesnt re render

I need to remove an item from my array state and it doesnt work the way I need it to. I get the state from a details obj from the server and save it to name. It is an array of objects.
const [name, setName] = useState(
[...details?.name] || []
);
My add function works as needed:
const addName = () => {
nameForm.validateFields().then(values => {
setName([...name, values]);
nameForm.resetFields();
setModalVisible(false);
});
};
The remove function however doesnt. Calling the function the first i works but every time I call that function again, it uses the initial declaration of the name state. Ex, if the array is size 4 first call would remove an element and it would be size 3. If i call that function again, the name is still size 4.
const removeName = (obj) => {
setName([...name.filter(i => i !== obj)]);
};
You cannot compare objects using the !== operator. Rather you need to compare some properties of the object. You did not say which properties your object has but let us assume they have an id property then:
setName([...name.filter(i => i.id !== obj.id)]);
would work.

How to use a constant in map return function ReactJS

I am wondering how can I use a constant in the map function, basically meaning: I have saved correctly the option I want from my falling menu regarding the constant (I checked it with console.log), for instance I have a name chosen and then I want to use it in the map function but unfortunately I get all the elements undefined when I use the constant; when I replace the constant with a directly written "name", I get all the elements correctly with their names.
Filterhosts=() =>{
var newState = this.state.array.slice(); // in the state array is empty
const selectedOption = this.state.selectedOption;
const writtenOption = this.state.writtenOption;
console.log(selectedOption) //ok
const namearray= this.state.filteredhosts.map(host=> {
return (
host.software.map((sub, subindex) => {
if(selectedOption=="name" || selectedOption=="vendor") {
newState.push(sub.selectedOption) //when I write sub.selectedOption , I receive empty array with all elements as undefined otherwise I become the names of all elements
}
else {
if(sub.vulnerable==true){
newState.push(sub.vulnerability.cve)}
}
})
)
})
const filteredarray = newState.filter( function(item){
return item === writtenOption // here I become properly the searched name//vendor
}
// how to show the whole info for the searched name/vendor(cpe, cve, cvss etc.)
)
console.log(newState); //ok
console.log(filteredarray); //ok
}
Oh I see.
sub.name
is the same as
sub["name"]
which is also the same as
sub[selectedOption]
IF selectedOption is "name". So just use newState.push(sub[selectedOption]) and I think that should work for you.

Categories

Resources