Index Item of array not getting deleted - reactjs

I've just noticed a problem with my UI where clicking the delete button at a particular index doesn't delete that particular record and instead ends up deleting data situated at a different index. I'm quite clueless as to why this is happening. The list seems to render properly in order but then it all goes south when I try deleting it. My first deduction of this issue for a split second was that this had something to do with the API that I'm fetching the data from but then I realized that the data was getting rendered as it should and as it was defined. I would like to know what I can do to ensure that I'm deleting the data that I've clicked and not anything else. Here is my code:
import { useEffect, useState } from "react";
const Task = () => {
const [todos, setTodos] = useState([]);
useEffect(() => {
fetch('http://jsonplaceholder.typicode.com/todos')
.then(res => res.json())
.then(data => {
setTodos(data)
})
}, []);
const deleteTodo = (index) => {
const temp = [...todos];
temp.splice(index, 1);
setTodos(temp);
console.log(index);
// console.log(`newData : ${arr} || newLength : ${arr.length}`);
console.log(`newLength : ${todos.length}`);
}
return (
<div className='container'>
<table className='table'>
<tbody>
{todos.map((key, value) => (
<tr key={key.id}>
<td>{todos[value].id}</td>
<td>{todos[value].title}</td>
<td>{`${todos[value].completed}`}</td>
<td>
<button className='btn btn-danger ' onClick={() => deleteTodo(key.id)}> Delete </button>
</td>
</tr>
))}
</tbody>
</table>
<button className='btn btn-primary'>Add Task</button>
</div>
);
}
export default Task;

You are passing key.id to deleteTodo and then delete the item in the todo with index of key.id and it may not as the same as the index of the item, you should pass the index of item to the deleteTodo
like this :
{todos.map((todoItem, todoIndex) => (
<tr key={todoItem.id}>
<td>{todoItem.id}</td>
<td>{todoItem.title}</td>
<td>{`${todoItem.completed}`}</td>
<td>
<button className='btn btn-danger ' onClick={() => deleteTodo(todoIndex)}> Delete </button>
</td>
</tr>
))}

Mehtod .splice() needs index of element as start index, but you are sending an id. So, if id is 1 you will remove element on position 1, what is other element.
You can do .findIndex of element by id inside your delete function.
There is working example Example

Related

How to retrieve only one record from a list in props? (Reactjs)

My problem is the following:
I have a list with map that searches the DB and shows in a table.
The user has the option to click to check the details of the record.
A modal opens and shows these details.
But, in the modal, it search brings the list of all records instead of being only the one chosen by the user.
I already tried to use stopPropagation inside my button function but, even so it continues.
useState modal
const [isModalTraineeDetailsVisible, setModalTraineeDetailsVisible] = useState(false);
Function to select the person's id in the table
const traineeSelectById = (e, listTrainee) => {
e.stopPropagation();
setModalTraineeDetailsVisible(true);
Table showing the map and button with their functions
// Creating a map with date from listTraineeData, commes to the API BDD
listTraineeData.map((listTrainee) => (
<StyledTableRow key={listTrainee.id}>
<StyledTableCell>{listTrainee.first_name}</StyledTableCell>
<StyledTableCell>{listTrainee.last_name}</StyledTableCell>
<StyledTableCell>{listTrainee.num_soci}</StyledTableCell>
<StyledTableCell>{listTrainee.addresse1}</StyledTableCell>
<StyledTableCell>{listTrainee.active}</StyledTableCell>
<StyledTableCell>
<Button
variant="contained"
size="small"
onClick={(e) =>
traineeSelectById(listTrainee)
}
>
{" "}
Details modal
</Button>
{isModalTraineeDetailsVisible ? (
<ModalTraineeDetails
data={listTrainee}
onClose={() => setModalTraineeDetailsVisible(false)}
>
<h2>Modal Details Trainee</h2>
</ModalTraineeDetails>
) : null}
Because you map all items on isModalTraineeDetailsVisible, so if it false, will hide all items, and if it is true it will show all items, You should use an array instead and map with the index.
Also, You don't need to use e.stopPropagation
I investigate your problem.
Definitely, the example is not the best.
Try something like this
import { render } from 'react-dom'
import { useState } from 'react'
function App() {
const [selectedTrainee, setSelectedTrainee] = useState()
const trainees = [
{
id: '1',
first_name: 'Chris',
last_name: 'Ch'
},
{
id: '2',
first_name: 'Paul',
last_name: 'Pa'
}
]
return (
<div>
<table>
<thead>
<tr>
<th>First name</th>
<th>Last name</th>
<th>Details</th>
</tr>
</thead>
<tbody>
{trainees.map(trainee => {
return (
<tr key={trainee.id}>
<td>{trainee.first_name}</td>
<td>{trainee.last_name}</td>
<td>
<button onClick={() => setSelectedTrainee(trainee)}>+</button>
</td>
</tr>
)
})}
</tbody>
</table>
{selectedTrainee && (
<Modal
trainee={selectedTrainee}
onClose={() => setSelectedTrainee(undefined)}
>
<h2>Modal Details Trainee</h2>
</Modal>
)}
</div>
)
}
function Modal(props) {
return (
<div>
{props.children}
<pre>{JSON.stringify(props.trainee)}</pre>
<button
onClick={props.onClose}
>
Close
</button>
</div>
)
}
render(<App />, document.getElementById('root'))
PS. You should be more careful about naming.
What I did here?
Firstly, I restructured your state.
You only need information about selectedTrainee.
Once you save the single trainee you can open the modal pretty easly.

How to use local storage in React js

This is my code:
function EditCourseTable() {
const [data, setData] = useState([]);
const [CourseID, setCourseID] = useState(0);
useEffect(() => {
Axios
.get("http://localhost:3003/coursestable")
.then(result => setData(result.data));
}, []);
return (
<div className="main">
<h2>
<table className="table" >
<thead className="thead-dark">
<tr>
<th scope="col">Course Number</th>
<th scope="col">Course Name</th>
<th scope="col">View Details</th>
<th scope="col">Edit Course</th>
<th scope="col">Delete Course</th>
</tr>
</thead>
<tbody>
{data.map((item, id) => {
return <tr key={id}>
{localStorage.setItem('CourseID', item.CourseID)}
<td>{item.CourseID}</td>
<td>{item.Name}</td>
<td>View</td>
<td><a href={`/editcourse2`} className="btn btn-primary" >
Edit</a></td>
<td><button className="btn btn-primary">Delete</button></td>
</tr>
})}
</tbody>
</table>
</h2>
</div>
)
}
export default EditCourseTable;
I use the localStorage to store the CourseId that the user click on (when click in Edit or View), but it is store the last courseID in the table, not the courseID that I click on. Whats the error?
You should insert the value in the localStorage by triggering a function called on click of an element
function storeCourse(id) {
localStorage.setItem('CourseID', id)
}
<td>
<span
className="btn btn-primary"
onClick={() => storeCourse(item.CourseID)}>
View
</span>
</td>
You need to create something to catch that click, so you can create some function like
const handleClickItem = (courseID) => {
localStorage.setItem('CourseID', courseID)
}
So whenever the user click, it will use onClick, so you can pass something like onClick = { () => handleClickItem(item.CourseID)} then pass the item.CourseID into that handleClickItem
Now the handleClickItem has the courseID
That's when you localStorage.setItem('CourseID', item.CourseID)
function EditCourseTable() {
const [data, setData] = useState([]);
const [CourseID, setCourseID] = useState(0);
useEffect(() => {
Axios
.get("http://localhost:3003/coursestable")
.then(result => setData(result.data));
}, []);
//- Add handleClickItem
const handleClickItem = (courseID) => {
localStorage.setItem('CourseID', courseID)
}
return
Inside the return, the map one, just add onClick where ever you want the user to click
for example:
<tr key={id} onClick = {() => handleClickItem(item.CourseID)}>
Your localStorage code runs when rendered so the last rendered item's id is saved to localStorage. You should use the function onClick.
<tbody>
{data.map((item, id) => {
return <tr key={id} onClick={() => localStorage.setItem('CourseID', item.CourseID)}>
<td>{item.CourseID}</td>
<td>{item.Name}</td>
<td>View</td>
<td><a href={`/editcourse2`} className="btn btn-primary" >
Edit</a></td>
<td><button className="btn btn-primary">Delete</button></td>
</tr>
})}
</tbody>
In your code you save data to local storage on items render. All items saves to local storage on key CourseID in render order.
Because of this after items render local storage CourseID value equal last item in rendered collection.
Right chose for solving this problem is saving data to local storage on link click.
But i think you does not need saving this data to local storage. React allow storing this in state.
Example for your code:
const [clickedCourseId, setClickedCourseId] = useState(null);
...
render (
...
{data.map((item, id) => {
return (
<tr key={id}>
<td>{item.CourseID}</td>
<td>{item.Name}</td>
<td>View</td>
<td><a href={`/editcourse2`} onClick={() => { setClickedCourseId(item.CourseID) }} className="btn btn-primary" >
Edit</a></td>
<td><button className="btn btn-primary">Delete</button></td>
</tr>
)
})}
In this example, when you click on View or Edit links, clickedCourseId being filled clicked item CourseId and you does not need to store it in localStorage.
However, if you want to store it in localStorage, you can change setClickedCourseId to your localStorage setItem

React.js: CheckOut Time is not showing (Array not updating)

Actually, I am making Employee attendance portal. I have 2 buttons on the Dashboard named as CheckIn & CheckOut. When I click the CheckIn button I get the current date, attendance as 'PRESENT' & CheckIn time from the Backend & when I click the checkOut button I get CheckOut time from the Backend. I just wanted to display all of these data in a table on React. I am getting all the data from the backend but I am unable to show the checkOut time in a table. What I am doing is I have made an array UseState with the name checkIn and I am putting all the data in that array. But in my scenario, only CheckIn data is pushed in the CheckIn array, and CheckOut data is not pushed in CheckIn array. On the table, I have mapped on Checkin array data. Getting the chechIn data & checkOut column remains empty.
import React, { useState, useEffect } from "react";
import moment from "moment";
import EmployeeNavbar from "./EmployeeNavbar";
import { useDispatch, useSelector } from "react-redux";
import {
employeeCheckIn,
employeeCheckOut,
getEmployeeCheckIn,
getEmployeeCheckOut
} from "../../actions/employeeActions.js";
const EmployeePanel = () => {
const employee = useSelector(state => state.employee);
const dispatch = useDispatch();
const [checkIn, setCheckIn] = useState([]);
const handleCheckIn = id => {
dispatch(employeeCheckIn(id));
};
const handleCheckOut = id => {
dispatch(employeeCheckOut(id));
};
console.log(employee);
useEffect(() => {
dispatch(getEmployeeCheckIn());
dispatch(getEmployeeCheckOut());
{
employee.checkOut
? setCheckIn(employee.checkIn, employee.checkOut)
: setCheckIn(employee.checkIn);
}
console.log(checkIn);
});
return (
<div>
<EmployeeNavbar />
<div className="container">
<h4>
<b>Employee Attendance Portal</b>
</h4>
<div>
<button
className="btn-small waves-effect waves-light hoverable green"
onClick={() => handleCheckIn(employee.employee.employeeData._id)}
>
Check In
</button>
<button
className="btn-small waves-effect waves-light hoverable red accent-4"
onClick={() => handleCheckOut(employee.employee.employeeData._id)}
>
Check Out
</button>
</div>
<table className="striped centered">
<thead>
<tr>
<th>Date</th>
<th>Attendance</th>
<th>Check In</th>
<th>Check Out</th>
</tr>
</thead>
<tbody>
{checkIn
? checkIn.map((list, i) => {
return (
<tr key={i}>
<td>{moment(list.date).format("MMM Do YYYY")}</td>
<td>{list.attendance}</td>
<td>{list.checkIn}</td>
<td>{list.checkOut}</td>
</tr>
);
})
: ""}
</tbody>
</table>
</div>
</div>
);
};
export default EmployeePanel;
Hi in this line you are only showing checkin data
{checkIn
? checkIn.map((list, i) => {
return (
<tr key={i}>
<td>{moment(list.date).format("MMM Do YYYY")}</td>
<td>{list.attendance}</td>
<td>{list.checkIn}</td>
<td>{list.checkOut}</td>
</tr>
);
})
: ""}
you should merge both checkedIn and checkedOut data into single arry.then it will work

Item in array getting deleted from top only

I have an array off which I would like to delete elements upon clicking the delete button. However, the problem with this is that only the data at the gets deleted no matter where I click leaving the data at that index intact. I would like to know what I can do to ensure this works normally. Below is my code:
import { useEffect, useState } from "react";
const Task = () => {
const [todos, setTodos] = useState([]);
useEffect(() => {
fetch('http://jsonplaceholder.typicode.com/todos')
.then(res => res.json())
.then(data => {
setTodos(data)
})
}, []);
//Using splice
// const deleteTodo = (index) => {
// const temp = [...todos];
// temp.splice(index, 1);
// setTodos(temp);
// console.log(index);
// // console.log(`newData : ${arr} || newLength : ${arr.length}`);
// console.log(`newLength : ${todos.length}`);
// }
//Using Filter
const deleteTodo = (index) => {
const newTodo = todos.filter(todo => todo.id !== index);
setTodos(newTodo);
console.log(`newLength : ${todos.length}`);
}
return (
<div className='container'>
<table className='table'>
<tbody>
{todos.map((key, value) => (
<tr key={key.id}>
<td>{todos[value].id}</td>
<td>{todos[value].title}</td>
<td>{`${todos[value].completed}`}</td>
<td>
<button className='btn btn-danger ' onClick={() => deleteTodo(key)}> Delete </button>
</td>
</tr>
))}
</tbody>
</table>
<button className='btn btn-primary'>Add Task</button>
</div>
);
}
export default Task;
I have tried both the splice and the filter methods.
The splice method deletes data only off the top irrespective of the data I delete whereas the filter method doesn't do anything at all as shown on the snippet below. The length remains the same even after clicking the delete button.
In .map() method the first argument - current array item, the second - index. You pass the current array item to your deleteTodo func, instead of passing id (deleteTodo(key.id)).
It should be like this:
const deleteTodo = (index) => {
const newTodo = todos.filter(todo => todo.id !== index);
setTodos(newTodo);
console.log(`newLength : ${todos.length}`);
}
return (
<div className='container'>
<table className='table'>
<tbody>
{todos.map((key, value) => (
<tr key={key.id}>
<td>{todos[value].id}</td>
<td>{todos[value].title}</td>
<td>{`${todos[value].completed}`}</td>
<td>
<button className='btn btn-danger ' onClick={() => deleteTodo(key.id)}> Delete </button>
</td>
</tr>
))}
</tbody>
</table>
<button className='btn btn-primary'>Add Task</button>
</div>
);
Also you don't need to do todos[value] as you already have a current item.
You could use this:
todos.map((item, index) => (<>
<td>{item.id}</td>
<td>{item.title}</td>
</>)

React: Updating multiple object variables in array

I want to implement a voting system in my app where you can vote thumbs-up or thumbs-down for a selected movie. The ratings will be saved in a ratings array of objects that contain movie title, id, thumbs-up votes and thumbs-down votes. If a movie has no votes, the first vote will add the new object into the ratings array, and subsequent votes will update the thumbs-up votes and thumbs-down votes of the object. My current code works for the first thumbs-up vote and adds the new obj into the array, but does not update the vote count for subsequent votes. How can I update the votes in the movie object for both thumbs-up and thumbs-down votes? This involves incrementing the count depending on whether the thumbs-up or thumbs-down button is clicked. How can I handle this besides creating separate but similar functions for each button like I’ve already started to do? Any help is greatly appreciated, thank you in advance!
const ThumbRating = ( {id, title} ) => {
const [thumbsUpCount, setthumbsUpCount] = useState(0);
const [thumbsDownCount, setthumbsDownCount] = useState(0);
const [ratings, setRatings] = useState([]);
const newUpVote = (id) => {
if (thumbsUpCount === 0 && thumbsDownCount === 0) {
setthumbsUpCount(thumbsUpCount + 1);
const obj = {
id: {id},
title: {title},
thumbsUpCount: thumbsUpCount +1,
thumbsDownCount: thumbsDownCount
}
setRatings([obj])
} else {
setthumbsUpCount(thumbsUpCount +1)
handleThumbsUp(id, thumbsUpCount +1)
}
}
const handleThumbsUp = (id, thumbsUpCount) => {
setRatings(ratings.map(obj => {
if (obj.id !== id) return obj
return {...obj, thumbsUpCount: thumbsUpCount +1}
}))
}
return (
<div className="thumb-rating">
<p>Would you recommend this movie?</p>
<table>
<tbody>
<tr>
<td>
<div >
<button className="thumbs-up" onClick={() => newUpVote(id)}>
<i className="fa fa-thumbs-up fa-4x" />
</button>
</div>
</td>
<td>
<div >
<button className="thumbs-down" onClick={() => setthumbsDownCount(thumbsDownCount + 1)}>
<i className="fa fa-thumbs-down fa-4x" />
</button>
</div>
</td>
</tr>
<tr>
<td>
<h2>Yes: {thumbsUpCount} </h2>
</td>
<td>
<h2>No: {thumbsDownCount} </h2>
</td>
</tr>
</tbody>
</table>
</div>
)
}
There is My solution:
The handlerHandleCountDown is working and the state is correcting updating.
Now you can try to handle the countDown based on the example.
Do not forget to pass id and title as props to your component. 🙂
import { useState } from 'react'
const ThumbRating = ({ id, title }) => {
const [thumbsUpCount, setthumbsUpCount] = useState(0)
const [thumbsDownCount, setthumbsDownCount] = useState(0)
const [ratings, setRatings] = useState([])
const newUpVote = (id) => {
if (thumbsUpCount === 0 && thumbsDownCount === 0) {
setthumbsUpCount(thumbsUpCount + 1)
const obj = {
id, // id:id also works but id: {id} will not
title,
thumbsUpCount: thumbsUpCount + 1,
thumbsDownCount: thumbsDownCount,
}
setRatings([obj])
} else {
setthumbsUpCount(thumbsUpCount + 1)
handleThumbsUp(id)
}
}
const handleThumbsUp = (id) => {
// Is better to use the find method when you want to find a single element in an array
const objectToBeUpdated = ratings.find((obj) => obj.id === id)
objectToBeUpdated.thumbsUpCount += 1 // I assumed you will always update by one
setRatings([objectToBeUpdated])
}
return (
<div className='thumb-rating'>
<p>Would you recommend this movie?</p>
<table>
<tbody>
<tr>
<td>
<div>
<button className='thumbs-up' onClick={() => newUpVote(id)}>
<i className='fa fa-thumbs-up fa-4x' />
</button>
</div>
</td>
<td>
<div>
<button
className='thumbs-down'
onClick={() => setthumbsDownCount(thumbsDownCount + 1)}
>
<i className='fa fa-thumbs-down fa-4x' />
</button>
</div>
</td>
</tr>
<tr>
<td>
<h2>Yes: {thumbsUpCount} </h2>
</td>
<td>
<h2>No: {thumbsDownCount} </h2>
</td>
</tr>
</tbody>
</table>
</div>
)
}
export default ThumbRating

Resources