Update item in state onClick ReactJS - reactjs

So, I have class Comopnent :
state = {
tokens: [
{
name: "first",
value: 3
},
{
name: "second",
value: 2
},
{
name: "third",
value: 4
}
]
}
handleClick = (name, id) => {
const newState = this.state.tokens.map((token => {
console.log(token.name)
}))
}
render() {
const token = this.state.tokens;
const tokenList = token.map(t => {
return (
<div value={t.name} onClick={() => this.handleClick(t.name, t.value)}>
<img src=""/>
</div>
)
})
What i need to do - after click - to subtract 1 from value clicked token.
So - ex. after click on "First" token i want his value equal 2.
So far I've done just as much as the above.
I do not know how to go about it, i am new in ReactJS, so thanks for help in advance!

You'll have to find in your state in tokens array the object which has the same name as the argument passed in the onclick handler. Then you will have to change it's value - decrement it (value--) but you have to be aware that you can't mutate the state.
handleClick = name => () => {
const { tokens } = this.state;
const clickedToken = tokens.find(token => token.name === name);
clickedToken.value--;
const clickedTokenIndex = tokens.indexOf(clickedToken);
const newTokens = [
...tokens.slice(0, clickedTokenIndex),
clickedToken,
...tokens.slice(clickedTokenIndex + 1)
];
this.setState({ tokens: newTokens });
};
Codesandbox link: https://codesandbox.io/s/92yz34x97w

First, some things are wrong with your code.
1- You have an array of tokens, then you're mapping the list, but you don't have a key to index, this will cause weird behaviors, I improve your tokens list with keys now.
2.- You can handle the click and change the state of the tokens list, this will trigger a reload of the component.
state = {
tokens: [
{
name: "first",
value: 3,
id: 1
},
{
name: "second",
value: 2,
id: 2
},
{
name: "third",
value: 4,
id: 3
}
]
}
handleClick = (name, id) => {
const { tokens} = this.state;
const newState = tokens.map((token => {
if(token.id === id) {
token.value--;
}
return token;
}))
}
render() {
const token = this.state.tokens;
const tokenList = token.map(t => {
return (
<div key={t.key} value={t.name} onClick={() => this.handleClick(t.name, t.value, t.key)}>
<img src=""/>
</div>
)
})

Related

Replacing value in nested state with react functional components

I am trying to use update state in a react function component but it is not working. I tried following a tutorial on pluralsite and apply it to my own project. Ideally this code should be finding the product based on the ID number and replacing the total with a new value.
Unfortunately I am getting an error saying that 'productData.find' is not a function and I'm not sure where the code being used for that is. Are there any suggestions on how to solve this issue?
This is what the data looks like. In this example I am saving the first element of the array.
export let data = [
{
name: "Name",
description:
"",
products: [
{
id: 1,
name: "Name 1",
material: 1.05,
time: 25,
total: 0,
},
{
id: 2,
name: "Name 2",
material: 3,
time: 252,
total: 0,
},
],
},
...
];
function CompareCard({}) {
const index = 0;
const [productData, setProductData] = useState(data[index]);
function setTotalUpdate(id) {
const productPrevious = productData.find(function (rec) {
return rec.id === id;
});
const productUpdated = {
...productPrevious,
total: 1,
};
const productNew = productData.map(function (rec) {
return rec.id === id ? productUpdated : rec;
});
setProductData(productNew);
}
setTotalUpdate(1)
}
It's because productData is not an array so .find would not work. You want iterate over the products property in your data, so do productData.products.find(...)
When you do
const [productData, setProductData] = useState(data[index])
you don't pass an Array on your state but an Object (the first element of your data so an Object) and Object don't have find method.
Try
const [productData, setProductData] = useState([data[index]])
with [] on our useState to put your Object on array
///////////////////////////////// Edit /////////////
Ok, I try your code, and I propose you this.
import React, { useState } from "react";
const data = [
{
name: "Name",
description: "",
products: [
{
id: 1,
name: "Name 1",
material: 1.05,
time: 25,
total: 0,
},
{
id: 2,
name: "Name 2",
material: 3,
time: 252,
total: 0,
},
],
},
];
const CompareCard = () => {
// setState with the subArray products from data[0], I use '...' (spread operator) inside a Array or an Object to make a shalow copy
const [productsData, setProductsData] = useState([...data[0].products]);
const setTotalUpdate = (id) => {
// find the product to change inside products collection, that's ok
const productPrevious = productsData.find((rec) => {
return rec.id === id;
});
// create a new product to change one property's value, you can do something like 'productPrevious.total = 1', same same
const productUpdated = {
...productPrevious,
total: 1,
};
// create a new products collection to update state
const productNew = productsData.map((rec) => {
return rec.id === id ? productUpdated : rec;
});
setProductsData([...productNew]);
};
const setTotalUpdateSecond = (id) => {
// create a newState
const newState = productsData.map((product) => {
// condition if id === productId and do something
if (id === product.id) {
product.total = 1;
}
// both case, I return the product (change or not)
return product;
});
setProductsData([...newState]);
};
return (
<>
<button onClick={() => setTotalUpdate(1)}>Test old method on product 1</button>
<button onClick={() => setTotalUpdateSecond(2)}>Test second method on product 2</button>
{productsData.map((product) => {
return (
<>
<p>Product Id : {product.id} / Product Total : {product.total}</p>
</>
);
})}
</>
);
};
export default CompareCard;
Can you copy / past this, try and say me if it's what you want, if yes, I explain you where the confusion was. If not, explain me, what's the problem here and I modificate.

Array data from Graphql is not populating a component in React

I am trying to render multiple checkboxes that have two functions:
Show two states: checked and unchecked
Update a checked array with the checked checkboxes
Currently, I am successfully able to accomplish these two goals with dummy data:
const dummyPlayers = [
{ id: 1, name: 'Puppa' },
{ id: 2, name: 'Korvo' },
{ id: 3, name: 'Jesse' },
{ id: 4, name: 'Terry' },
{ id: 5, name: 'Gobblins' }
]
This is the shape of the array I want to populate the checkboxes with:
[
{
"id": "936d6050-00df-4bd4-bc54-6ce58ad0210c",
"name": "Travis",
"owner": "moralesfam",
"type": "Member",
"createdAt": "2021-09-24T20:08:02.292Z",
"updatedAt": "2021-09-24T20:08:02.292Z"
}...
]
However, when I start pulling data in from a database with Graphql, while I am able to render the checkboxes to the DOM, they are not interactive (don't show checked state) and don't log the checked checkboxes.
I bring in the data, an array of objects through a custom React hook, called useMembers and the data is stored in a members array. Console logging members prints out the array, but as soon as I swap the dummyPlayers for the members array, the two goals I stated earlier are unsuccessful.
// RecordGame.js
import React, { useState } from 'react'
import useLoadMembers from '../hooks/useLoadMembers'
import useUser from '../hooks/useUser'
function RecordGame() {
const dummyPlayers = [
{ id: 1, name: 'Puppa' },
{ id: 2, name: 'Korvo' },
{ id: 3, name: 'Jesse' },
{ id: 4, name: 'Terry' },
{ id: 5, name: 'Gobblins' },
]
const { members } = useLoadMembers(updateLoading)
const { user } = useUser()
const [checkedState, setCheckedState] = useState(
new Array(members.length).fill(false)
)
let playingPlayers = []
for (var index in checkedState) {
if (checkedState[index] === true) {
playingPlayers.push(dummyPlayers[index])
}
}
console.log(playingPlayers)
const handleOnChange = (position) => {
const updatedCheckedState = checkedState.map((player, index) =>
index === position ? !player : player
)
setCheckedState(updatedCheckedState)
}
// Rendered elements
const playerCheckboxes = dummyPlayers.map((player, index) => {
return (
<div key={index}>
<label htmlFor={player.name}>
<input
type="checkbox"
id={player.name}
name={player.name}
checked={checkedState[index]}
onChange={() => handleOnChange(index)}
/>
<span> {player.name}</span>
</label>
</div>
)
})
return (
<div>
<form>
{/* Game Players */}
<div>
<label htmlFor="players">
Who Played?
</label>
<div>{playerCheckboxes}</div>
</div>
</form>
</div>
)}
</Dashboard>
)
}
export default RecordGame
//useLoadMember.js
import { useState, useEffect } from 'react'
import { API, Auth } from 'aws-amplify'
import { listMembers } from '../graphql/queries'
const useLoadMembers = (updateLoading) => {
const [members, updateMembers] = useState([])
useEffect(() => {
fetchMembers()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const fetchMembers = async () => {
try {
let memberData = await API.graphql({
query: listMembers,
variables: { limit: 100 },
})
updateLoading(false)
let allMembers = memberData.data.listMembers.items
setFilteredMembers(allMembers)
} catch (err) {
console.error(err)
}
}
const setFilteredMembers = async (allMembers) => {
const { username } = await Auth.currentAuthenticatedUser()
const myMemberData = allMembers.filter((p) => p.owner === username)
updateMembers(myMemberData)
}
return { members }
}
export default useLoadMembers
In this first, picture, I used the dummyPlayers array and got the results I wanted.
However, in this second screenshot, I replaced the dummyData with the members array and did not get any results I wanted.
I'm just confused on why I am getting different results with the same array shape.
wherever you use members you will need to check that members is not undefined before it gets used. If the api calls are not complete, it will get set initially as undefined.
eg: members && members.map(() => ....)

react error "Expected an assignment or function call and instead saw an expression" under the map iteration

I am trying to move keys and values in propsValue to Users using immer.
Furthermore, there are 2 buttons.
"convert them" moves propsValue into a new Array.
"print all" prints all key and value of the new Array.
In my theory, it should be worked perfectly and keys and values in propsValue were moved into
users.
However, it prints "Expected an assignment or function call ..." when the button clicked.
please share your kind advice to solve this problem.
import React from 'react';
import { useImmer } from "use-immer";
const UserImmer = () => {
const propsValue = [
{
value: "Name", key:"first_name"
},
{
value: "last Name", key:"last_name"
},
{
value: "E mail", key:"e_mail"
},
{
value: "Address", key: "address"
}
]
const [user, setUser] = useImmer({
name: '',
key: '',
})
const [users, setUsers] = useImmer([])
const onbuttonChange = () => {
//iteration begins
propsValue.map(data => (
//moves propsValue key, value into user
setUser(draftState => {
draftState.name = data.name,
draftState.key = data.key
}),
//push new array key and value into Users
setUsers(draftState => {
draftState.push(user);
}),
//initialize the array
setUser(draftState => {
draftState.name = "",
draftState.key = ""
})
) )
}
const onClickUser = () => {
users.map(temp => console.log(temp.name, " : " , temp.key));
}
return (
<div>
<button onClick={onClickUser}>print All</button>
<button onClick={onbuttonChange}>Convert them</button>
</div>
)
}
export default UserImmer;
found a solution. modified some comma and semi-colon
propsValue.map(data => (
//moves propsValue key, value into user
setUser(draft => {
draft.name = data.name;
draft.key = data.key;
}),
setUsers(draft => {
draft.push(user);
}),
setUser(draft => {
draft.name ="";
draft.key = "";
})
) )

REACT UPDATE A DATA

I have a problem trying to update an Array of Objects that lives in a Themecontext, my problem is with mutation, I'm using Update from Immutability helpers. the thing is that when I update my array in my specific element, This appears at the end of my object.
This is my code:
function changeValueOfReference(id, ref, newValue) {
const namevalue = ref === 'colors.primary' ? newValue : '#';
console.warn(id);
const data = editor;
const commentIndex = data.findIndex(function(c) {
return c.id === id;
});
const updatedComment = update(data[commentIndex], {styles: { value: {$set: namevalue} } })
var newData = update(data, {
$splice: [[commentIndex, 1, updatedComment]]
});
setEditor(newData);
this is my result:
NOTE: before I tried to implement the following code, but this mutates the final array and break down my test:
setEditor( prevState => (
prevState.map( propStyle => propStyle.styles.map( eachItem => eachItem.ref === ref ? {...eachItem, value: namevalue}: eachItem ))
))
Well, I finally understood the issue:
1 - commentIndex always referenced to 0
The solution that worked fine for me:
1 - Find the index for the Parent
2 - Find the index for the child
3 - Add an array []
styles : { value: {$set: namevalue} } => styles :[ { value: [{$set: namevalue}] } ]
Any other approach is Wellcome
Complete Code :
function changeValueOfReference(id, referenceName, newValue) {
const data = [...editor];
const elemIndex = data.findIndex((res) => res.id === id);
const indexItems = data
.filter((res) => res.id === id)
.map((re) => re.styles.findIndex((fil) => fil.ref === referenceName));
const updateItem = update(data[elemIndex], {
styles: {
[indexItems]: {
value: { $set: namevalue },
variableref: { $set: [''] },
},
},
});
const newData = update(data, {
$splice: [[elemIndex, 1, updateItem]],
});
setEditor(newData);
}

Handling multiple textfield with single onChange react hooks

I have some issues with managing controls over some textfields.
I have an object
let obj = {
[
{
name:"a",
adress:[{"foo":a1,"bar":a1}],
comment:"aaaaa"
},
{
name:"b",
adress:[{"foo":b1,"bar":b1}],
comment:"bbbbb"
}
]
}
And I generate dynamic textfields
{obj.map((item, i) => {
return <TextField name = {item.name} value={item.comment} onChange={(e)=>{handleComment(e,item.name)}}/>
})}
I want to modify comment for every specific textfield based by its name
const handleComment = (e,name) =>{
e.preventDefault();
setObject({...obj,comment:e.target.value})
}
I can't managed the right way to do it. I can't figure it out how to do it. If you guys can help me, it would be awesome. Thanks!
Ciao, you could use an array in handleComment function in which you can copy current state. Then update that array and set the state.
Lets say you have:
const obj = [
{
name: "a",
adress: [{ foo: "a1", bar: "a1" }],
comment: ""
},
{
name: "b",
adress: [{ foo: "b1", bar: "b1" }],
comment: ""
}
];
const [object, setObject] = useState(obj);
and return like:
return object.map((item, i) => {
return (
<TextField
name={item.name}
value={item.comment}
onChange={(e) => {
handleComment(e, item);
}}
/>
);
});
Then your handleComment becomes:
const handleComment = (e, item) => {
e.preventDefault();
let result = object; // copy state
result = result.map((el) => { // map array to replace the old comment with the new one
if (el.name === item.name) el.comment = e.target.value;
return el;
});
setObject(result); // set state with new comment
};
Here a codesandbox example.

Resources