How do I update a specific index - javascript

So I'm practising react with a simple task master app, where I add each user input as a new task in an array of tasks. In the app, I have Add, Delete, and Update buttons.
Everything seems to be working fine except for the update function, it updates the last index of the array instead of the specific index I clicked.
Here is my JSX
const JsxElement = task.map((eachTask, index) => {
return (
<Fragment key={index}>
<div key={index} className="table-data-container">
<div className="item-data">{eachTask}</div>
<div className="item-data">{date}</div>
<div className="item-data">
<div className="btn-data-container">
<div className="btn-data">
<div className="btn" onClick={() => deleteTask(index)}>Delete</div>
</div>
<div className="btn-data">
<div className="btn" onClick={() => UpdateTaskBtn(eachTask, index)}>Update</div>
</div>
</div>
</div>
</div>
<br/>
{task.length - 1 === index &&
<div className="input-update-container">
<div className="input-area">
<input
ref={inputRef}
type="text"
/>
</div>
<div className="btn-update-add-container">
{update ?
<div className="btn-add" onClick={() => handleTaskUpdate(eachTask, index)}>Update
Task</div>
:
<div className="btn-add" onClick={handleTask}>Add Task</div>
}
</div>
</div>
}
</Fragment>
)
})
The first update button function prepares the input, sets the task to be updated and makes the update button visible. The second one is where I want the update action to happen once clicked.
function UpdateTaskBtn(eachTask) {
inputRef.current.value = eachTask
setUpdate(true)
}
function handleTaskUpdate(e, index) {
const list = [...task]
list[index] = inputRef.current.value
setTask(list)
inputRef.current.value = ""
setUpdate(false)
}
I want to be able to set the task to the specific index I want to update.

based on this line of code
{task.length - 1 === index &&
you are checking if the index is the last index , so you are passing the last index to the handleTaskUpdate function. so you can define a updateIndex state
const [updateIndex,setUpdateIndex] = useState()
then your function should look like this
function UpdateTaskBtn(eachTask,index) {
inputRef.current.value = eachTask
setUpdateIndex(index)
setUpdate(true)
}
function handleTaskUpdate(e, index) {
const list = [...task]
list[updateIndex] = inputRef.current.value
setTask(list)
inputRef.current.value = ""
setUpdate(false)
}
do not forget to pass index to UpdateTaskBtn function in onClick event

Try this
function handleTaskUpdate(e, index) {
const value = inputRef.current.value
const updatedTasks = task.map((task, i) => i === index ? value : task)
setTask(updatedTasks)
inputRef.current.value = ""
setUpdate(false)
}

Related

Make a button expand and collapse in map function in ReactJS

I am working on a project as a means to practice some stuff in react and I need to render a button for each of the map data. I did this successfully but expand and collapse has been giving me issue. Whenever I click on the button all data collapse and expand together.
const DataFetch = () => {
...
const [btnValue, setBtnValue] = useState('+');
const handleChange = (e) => {
setShowData(!showData);
setBtnValue(btnValue === '+' ? '-' : '+');
};
return (
<div className='container'>
...
{studentResults
.filter((val) => {
if (searchTerm === '') {
return val;
} else if (
val.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
val.lastName.toLowerCase().includes(searchTerm.toLowerCase())
) {
return val;
} else {
return null;
}
})
.map((student) => {
return (
<div key={student.id}>
<div className='card'>
<div className='row'>
<div className='col-2'>
<div className='pic'>
<img src={student.pic} alt='avatar' />
</div>
</div>
<div className='col'>
<div className='details'>
<p className='name'>
{student.firstName.toUpperCase()}{' '}
{student.lastName.toUpperCase()}
</p>
<div className='sub-details'>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>
Average:{' '}
{student.grades.reduce(
(a, b) => parseInt(a) + parseInt(b),
0
) /
student.grades.length +
'%'}
</p>
<button onClick={handleChange} className='showBtn'>
{btnValue}
</button>
{showData && (
<div>
<br />
{student.grades.map((grade, key) => {
return (
<p key={key}>
Test {key + 1}:   {grade}%
</p>
);
})}
</div>
)}
...
Collapse Image
Expand Image
All the elements expand and collapse together because you assign to all of them the same state showData state.
One solution would be to add a new field to your data (so inside student) that is true or false when you want to expand or collapse the single student.
Another solution would be to create the showData state as an array where each element correspond to a different student. When you click the button, in this case, you pass to the function for example the id and with that you link your student to the right element inside the showData.

How can delivered array in event function on javascript with React?

There is two screen. First picture is show list. If clicked Button which is next to search Button, second picture is showed. Second is select filter. When user select options in filter and clicked "적용" Button, list will be changed.
And there code is here.
...
const [modalOpen, setModalOpen] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [postsPerPage] = useState(6);
const [area] = useState(location.state.area)
const [city] = useState(location.state.city)
// all list
const locList = restList.filter(
key => (key.address.includes(area) && key.address.includes(city))
);
//about Modal
const openModal = () => {
setModalOpen(true);
}
const closeModal = () => {
setModalOpen(false);
}
...
let target;
let foodList = [[], []]
const sel_Food = (e) => {
if (e.target.tagName === "BUTTON") {
target = e.target.innerText;
if (!foodList.includes(target))
foodList[0].push(target)
} else {
target = e.target.parentNode.nextSibling.innerText;
foodList[1] = target
}
}
//make list
const list = (foodList) => {
let result = [];
if (foodList[0].length === 1) {
result = locList.filter(key => key.foodType.includes(foodList[0][0]))
} else {
for (let i = 0; i < foodList[0].length; i++) {
let tmp = locList.filter(key => key.foodType.includes(foodList[0][i]))
result.push(...tmp)
}
}
if (foodList[1].length != 0) {
result = result.filter(key => key.friendly.includes(foodList[1]))
}
return result
}
return (
<div>
... {
(locList.length === 0)
? ...
: <div>
<div className="btn_class_list">
...
<React.Fragment>
<button className="button_filter" onClick={openModal}><img className="icon_filter" src={icon_filter} alt="icon_filter"/></button>
<Modal_Restaurant open={modalOpen} close={closeModal} header="필터">
<p className="langoption">음식 종류</p>
<div>
...
</div>
<p className="langoption">Halal Standard</p>
<div className="foodtype">
...
</div>
</Modal_Restaurant>
</React.Fragment>
</div>
<Item rlist={currentPosts(locList)} moveTo="restaurant" area={area} city={city}></Item> //make List
<Pagination start={numOfFirst} last={numOfLast} paginate={setCurrentPage}></Pagination>
</div>
}
</div>
)
sel_Food function record option that user selected.
list function make sub list for delivered to Item component.
But I don't know how to delivered array result in list function with "적용" button.
Thank you.
As I understand from the question, you need to change the list of items you display according to the filters the user applies. It can be done with the state. As a start, you can try to update the list of items through a filter function and use the state variable to render the filtered results. You'd asked about returning a value when a button is clicked. Buttons have an onClick handler in React. You can pass it a function like this:
<Button onClick={handleClick} />
or if your function takes any arguments:
<Button onClick={() => handlClick(args)} />

React - Last list element removed when deleting from the middle

I am trying to implement a list composed of an <input> field as well as a delete button, for each item. I also have an Add item button near the list so the user can add new entries.
I am encountering an issue when trying to delete a certain item from the list.
For example:
The initial list
index
input-value
1
a
2
b
3
c
4
d
5
e
After deleting the item at index 3
index
input-value
1
a
2
b
4
c
5
d
As you can observe, the right item is deleted, but the contents are somehow shifted from their initial place and it appears like the last item is always deleted.
I can't figure it out what am I doing wrong.
Here is the parent component:
export default function ListPage() {
const [itemList, setItemList] = useState([]);
const removeItem = (index) => {
console.log(index);
const list = Object.assign([], itemList);
const updatedList = list.filter((object, i) => i != index);
setItemList(updatedList);
console.log(updatedList);
};
const addItem = () => {
const updatedList = [...itemList, <ListElement />];
setItemList(updatedList);
console.log(updatedList);
};
return (
<div className="list-page">
<div className="list-page-title">
<h2>Create your own List</h2>
<br></br>
<h4>Create your own list</h4>
</div>
<input
className="title-input"
type="text"
placeholder="Enter Title"
/>
<div className="list-content">
{itemList.map((data, index) => {
return (
<div>
<ListElement
handleRemove={() => removeItem(index)}
index={data.index}
/>
</div>
);
})}
</div>
<button className="add-button" onClick={addItem}>
<span className="list-page-button-icon">+</span>Add Item
</button>
</div>
);
}
And here is the ListElement component:
export default function ListElement(props) {
return (
<div className="list-item">
<input
className="item-input"
type="text"
placeholder={props.index}
></input>
<MdRemoveCircleOutline
className="remove-button"
onClick={props.handleRemove}
/>
</div>
);
}
Inside your removeItem function, you don't need to do const list = Object.assign([], itemList);; which is messing the order of how the list is stored and rendered.
You already have access to the entire list in the itemList variable you defined before.
So, instead of
const removeItem = (index) => {
console.log(index);
const list = Object.assign([], itemList);
const updatedList = list.filter((object, i) => i != index);
setItemList(updatedList);
console.log(updatedList);
};
So, instead of
const removeItem = (index) => {
const updatedList = itemList.filter((object, i) => i != index);
setItemList(updatedList);
};

can't read else part

I want my divs to check/uncheck.
But, else part can't read my code (undefined)
Plz help me...
(First time click is fine
second time click is undefined)
import React, { useState } from "react";
import "./Blocking.css";
const Blocking = () => {
const [checked, setChecked] = useState([false, false, false, false, false]);
const onCheck = (e) => {
e.preventDefault();
console.log(e.target.getAttribute("name"));
if (checked[e.currentTarget.getAttribute("name")] === false) {
e.target.style.background = "aquamarine";
setChecked(!checked[e.target.getAttribute("name")]);
console.log("if " + checked[e.target.getAttribute("name")]);
} else {
e.target.style.background = "white";
setChecked(!checked[e.target.getAttribute("name")]);
console.log("else " + checked[e.target.getAttribute("name")]);
}
};
return (
<div className="container">
<div className="items" name="0" onClick={onCheck}>
1
</div>
<div className="items" name="1" onClick={onCheck}>
2
</div>
<div className="items" name="2" onClick={onCheck}>
3
</div>
<div className="items" name="3" onClick={onCheck}>
4
</div>
<div className="items" name="4" onClick={onCheck}>
5
</div>
</div>
);
};
export default Blocking;
Below code block is console.log in chrome)
[HMR] Waiting for update signal from WDS...
Blocking.jsx:9 2
Blocking.jsx:14 if false
Blocking.jsx:9 2
Blocking.jsx:18 else undefined
1st i click any blocks then, print fine.
2nd i click any blocks then, print undefined.
The error is in:
setChecked(!checked[e.target.getAttribute("name")]);
The "checked" array is being replaced by a boolean.
You could do it as:
let newChecked = [...checked]; //Destructure
let index = e.currentTarget.getAttribute("name") //Get index
newChecked[index] = !checked[index] //Toggle check
setChecked(newChecked)
Issues
You are mutating your checked state array. setChecked(!checked[e.target.getAttribute("name")]); mutates the state to a single boolean, so subsequent checked[e.currentTarget.getAttribute("name")] conditional tests will likely always be falsey.
You are also directly manipulating the DOM. This is an anti-pattern in React.
You need to shallow copy the entire array and update the specific index. Use the state value to derive the background color.
const onCheck = (e) => {
e.preventDefault();
const index = Number(e.currentTarget.getAttribute("name"));
setChecked(checked => checked.map((val, i) => i === index ? !val : val));
};
return (
<div className="container">
<div
className="items"
style={{ background: checked[0] ? 'white' : 'aquamarine' }}
name="0"
onClick={onCheck}
>
1
</div>
...etc
</div>
);

select tag is keeping initial state value and not updating

I'm having some issues updating a select tag value. When I click on it and try changing the value it logs the new value to the console, but it's value is not changing no matter how many times I click a different option. But when I make some changes in the code editor and save it it display the new value but if I try againg changing the value clicking on it it does not change.
const quantityChange = (e) => {
console.log('changing quantity')
const newCart = shoppingCart
console.log(
"new cart variable created. It's value is " + JSON.stringify(newCart)
)
const index = e.target.dataset.index
console.log('Item index is ' + index)
newCart[index]['quantity'] = e.target.value
console.log(
'The new quantity for the item is ' + newCart[index]['quantity']
)
setShoppingCart(newCart)
console.log(
'new value set for shopping cart. ' +
JSON.stringify(shoppingCart)
)
}
const ShoppingCart = ({ shoppingCart, quantityChange, removeFromCart }) => {
const onSubmit = (e) => {
e.preventDefault()
}
if (shoppingCart.length === 0) {
return <div>There's no items in the shopping cart</div>
}
const options = []
for (let i = 0; i <= 10; i++) {
options.push(
<option name="quantity-1" value={i} key={'qsfkajlf' + i}>
{i}
</option>
)
}
return (
<div>
<h1>
There's {shoppingCart.length} item{shoppingCart.length > 1 && 's'} in
your shopping cart.
</h1>
{shoppingCart.map((item, index) => {
const { id, name, price, image, quantity } = item
return (
<div key={id}>
<div className="flex-container">
<div className="img-container">
<img src={image} alt="" />
</div>
<div className="item-info">
<h2>{name}</h2>
<p>${price}</p>
</div>
<div className="remove-quantity">
<div className="quantity">
<form onSubmit={onSubmit}>
{/* TODO: don't allow more than 10 items */}
<select
id="quantity"
data-index={index}
onChange={quantityChange}
value={quantity}
>
{options}
</select>
</form>
</div>
<div className="remove-container">
<button data-remove={index} onClick={removeFromCart}>
x
</button>
</div>
</div>
</div>
</div>
)
})}
</div>
)
}
It isn't updating because you are doing this:
const quantityChange = (e) => {
console.log('changing quantity')
const newCart = shoppingCart // <-- copy a reference to shoppingCart
// do stuff
setShoppingCart(newCart) // <-- write the original (modified) object back in
}
As the object reference doesn't change, the re-render isn't triggered.
Instead, you just need to copy your object (instead of just copying the reference):
const quantityChange = (e) => {
console.log('changing quantity')
const newCart = {...shoppingCart} // <-- copy the contents into a new object

Categories