Item in array getting deleted from top only - reactjs

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>
</>)

Related

How to toggle in a map function in React?

I try to load user history and show it inside a table.
Inside this table should be a button with "show more"
This button is a dropdown toggle button.
Everything working so far but when i click the button that is inside a map function all dropdowns open at the same time.
I know i've to use an index in the map function but I've no clue how i can use this index for my dropdown state.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { useSelector } from 'react-redux';
function OrderHistory() {
const [history, setHistory] = useState([]);
const [dropdown, setDropdown] = useState(false);
const auth = useSelector((state) => state.auth);
const token = useSelector((state) => state.token);
const { user } = auth;
useEffect(() => {
const getHistory = async () => {
const res = await axios.get('/user/history', {
headers: { Authorization: token },
userid: user._id,
});
setHistory(res.data);
};
getHistory();
}, [token, setHistory]);
console.log(history);
const clickHandler = (index) => (event) => {
//event.preventDefault();
setDropdown(!dropdown);
console.log('clicked');
};
return (
<div className='history-page h-screen'>
<h2>History</h2>
<h4>You have {history.length} ordered</h4>
<table>
<thead>
<tr>
<th>Bestellnummer</th>
<th>Datum</th>
<th>Summe</th>
<th>Details</th>
</tr>
</thead>
<tbody>
{history.map((items, index) => (
<tr key={items._id}>
<td>
{items._id}
</td>
<td>{new Date(items.createdAt).toLocaleDateString()}</td>
<td>
{(
Math.round(
items.cartItems.reduce((x, item) => x + item.price, 0) * 100
) / 100
).toFixed(2)}
€
</td>
<td>
<button
class='bg-yellow-500 hover:bg-yellow-400 text-white font-bold py-2 px-4 rounded'
key={index}
onClick={clickHandler(index)}
>
Show more
</button>
<div className={dropdown ? '' : 'hidden'}>
<div>{items.firstName}</div>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default OrderHistory;
Initialize the dropdown state with null.
const [dropdown, setDropdown] = useState(null);
In clickHandler store, i just store the index.
const clickHandler = (index) => {
setDropdown((prev) => {
return prev === index ? null : index;
});
console.log('clicked', index);
};
Now in map function, check where you apply the condition to show the dropdown. I check if the dropdown is equal to that index, then that dropdown will visible otherwise add hidden class.
You can also use _id to show/hide dropdown instead of index
<td>
<button
class='bg-yellow-500 hover:bg-yellow-400 text-white font-bold py-2 px-4 rounded'
key={index}
onClick={() => clickHandler(index)}
>
Show more
</button>
<div
className={dropdown === index ? '' : 'hidden'}
// style={{ display: dropdown === index ? 'block' : 'none' }}
>
<div>{items.firstName}</div>
</div>
</td>

Why onClick can't trigger the delete function?

What I'm trying to do is when the user clicks the delete button to delete the client, I want to show a modal that asks: Are you sure you want to delete it, yes or no.
The modal is on another component, and I thought to pass the delete function as props, but when I call the function in onClick method in the yes button, it won't delete.
ClientList.js
export default function ListClients() {
const [showModal, setShowModal] = useState();
const [userlist, setUserlist] = useState([]);
function deleteClient() {
const userParams = {
clientName:
clientName,
country: country,
clientid: selectedID,
};
axios
.delete(process.env + "client", {
data: clientParams,
})
.then((response) => {
setClientlist(clientlist.filter((client) => client.id !== clientId));
})
.catch((error) => {
console.log(error);
});
}
return(
<div>
<tbody>
{userlist.length > 0 ? (
userlist.map((userlist) => (
<tr key={userlist.id}>
<td>
<div">
{userlist.id}
</div>
</td>
<td>
<button type="button" onClick= () => setShowModal(true)}>
Delete
</button>
</td
</tr>
</tbody>
<ModalDelete showModal={showModal} setShowModal={setShowModal} onDel={() => deleteClient(clientlist.id)}/>
</div>
);
ModalDelete.js
export default function ModalDelete({ showModal, setShowModal, onDel}) {
return(
<div>
{ showModal ? <Transition.Root show={showModal}>
<div>
<p> Are you sure you want to delete the client?</p>
</div>
<div>
<button type="button" onClick={() => onDel()}>Yes</button>
<button type="button" onClick={() => {setShowModal(false);}} >
Go Back
</button>
</div>
</Transition.Root> : null }
</div>
);
}
Not sure why the client is not deleted
ModalDelete.js
onClick={() => onDel()}
shoud be
onClick={onDel}

Index Item of array not getting deleted

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

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

Sort the table ascending & descending order when i click on button in reactjs

function TopicsTable(props) {
return (
<Table className="tablecss">
<thead>
<tr>
<th>
<button type="button">
{props.getHeader[0]}
</button>
</th>
<th>
<button type="button">
{props.getHeader[1]}
</button>
</th>
<th>
<button type="button">
{props.getHeader[2]}
</button>
</th>
<th>
<button type="button">
{props.getHeader[3]}
</button>
</th>
<th>
<button type="button">
{props.getHeader[4]}
</button>
</th>
<th>
<button type="button">
{props.getHeader[5]}
</button>
</th>
</tr>
</thead>
<TableBody>
{(props.getRowInfo)}
</TableBody>
</Table>
);
}
getRowsData = () => {
var rowContents = this.state.data;
return rowContents.map((rowContent, index) => {
this.state.rowCount = this.state.rowCount + 1;
return <React.Fragment><TableRow key={index} onClick={() => this.handleClick(index)}>{this.RenderCell(rowContent)}</TableRow> {this.state.show && this.state.id === index ? <ChildComponent/>:""} </React.Fragment>
})
}
render() {
return (
<div className="Main">
<header className="App-header">
<h2 className="hdr"><i>Openion</i></h2>
</header>
<h4><Actionunit /></h4>
<br />
<TopicsTable getHeader={this.getHeader()} getRowInfo={this.getRowsData()}/>
</div>
);
}
Sort the table ascending & descending order when i click on button in reactjs
I think the requirements you're looking for is:
dynamically select which column is to be sorted
sort ascending or descending
the number of columns can be dynamic
the number of rows can be dynamic
My solution doesn't use <table> but that is something you can manage easily...
Relevant component which gets headers, rows... sorts and prints data:
import React, { useState, useEffect } from "react";
export const MyTable = props => {
const { header, data } = props;
const [sortedRows, setSortedRows] = useState([]);
const [headers, setHeaders] = useState([]);
useEffect(() => {
setSortedRows(data);
setHeaders(header);
}, []);
const Sorter = fieldToSort => {
let sortedArray = sortedRows;
if(fieldToSort.dir === 'asc' ){
sortedArray.sort((a, b) =>
a[fieldToSort.label] > b[fieldToSort.label] ? 1 : b[fieldToSort.label] > a[fieldToSort.label] ? -1 : 0
);
} else {
sortedArray.sort((a, b) =>
a[fieldToSort.label] < b[fieldToSort.label] ? 1 : b[fieldToSort.label] < a[fieldToSort.label] ? -1 : 0
);
}
(fieldToSort.dir === 'asc') ? fieldToSort.dir = 'dsc' : fieldToSort.dir = 'asc';
let newHeaders = header;
newHeaders[fieldToSort.dir] = fieldToSort.dir;
setHeaders([...newHeaders]);
setSortedRows([...sortedArray]);
};
return (
<>
<h2>Actual Table</h2>
{headers.map((val, ind) => {
return (
<button type="button" key={ind} onClick={() => Sorter(val)}>
{val.label} ({val.dir})
</button>
);
})}
{sortedRows.length > 0 ? (
<ul>
{sortedRows.map((val, ind) => {
return (
<li>
{val.name} - {val.age}{" "}
</li>
);
})}{" "}
</ul>
) : (
"no data available"
)}
</>
);
};
complete working stackblitz here
First, define your state, to save your current sort order:
state = {
sortedBy: '',
sortDirection: 0
}
Then, define constants, to show the order direction (outside of your class):
const sortAsc = 1;
const sortDesc = -1;
Then, define your sort function:
sortTable = (columnName) => {
let sortDirection = sortAsc;
if (this.state.columnName === columnName) {
if (this.state.sortDirection === sortAsc) {
sortDirection = sortDesc;
}
}
this.s.TableData.sort((x1, x2) => x1[columnName] < x2[columnName] ? -1 * sortDirection : sortDirection )
this.setState({
columnName, sortDirection, data: this.state.data
})
};
Then, attach this function to each table header:
<th>
<button type="button" onClick={() => props.sortTable(props.getHeader[0])> // <-- here you pass the header NAME (Upvote, Downvote, Comment and etc.)
{props.getHeader[0]}
</button>
</th>
Keep in mind, that this code is taken out of context, and you may face errors with naming, parameters passing and etc, in general it is a basic abstraction. The sort function was tested out and working. I hope you will manage to integrate it in your application. Good luck!

Resources