Removing Item from DOM display (React) - reactjs

sample image of page so far
Please see the above image. I'm wanting to be able to remove the string in the "skill" column when it is deleted after clicking the "Delete" button on the right. I can delete an Employee using filter() with the Delete button on the left. This removes the entire row from the DOM, which is great, but I would like the Delete button on the right to simply remove the content from the Skill(s) column for that particular employee.
I have tried doing something similar in the deleteSkill() function, but I am not sure how to remove the content of the Skill(s) column without deleting the entire row. The issue lies in my setList() function within my deleteSkill() function. Any ideas would be greatly appreciated.
Here is the code:
import React, { Fragment, useEffect, useState } from 'react';
const List = () => {
const [list, setList] = useState([]);
//DELETE Employee by ID
const deleteEmployee = async (id) => {
try {
const deleteEmployee = await fetch(`http://localhost:5000/employees/${id}`, {
method: "DELETE"
});
setList(list.filter(item => item.employee_uuid !== id));
} catch (err) {
console.error(err.message)
}
};
//DELETE Skill by ID
const deleteSkill = async (id) => {
try {
const deleteSkill = await fetch(`http://localhost:5000/employees/${id}/skills`, {
method: "DELETE"
});
setList(list.filter(item => item.summary !== id));
} catch (err) {
console.error(err.message)
}
};
const getList = async () => {
try {
const response = await fetch("http://localhost:5000/employees")
const jsonData = await response.json();
setList(jsonData);
} catch (err) {
console.error(err.message);
}
};
useEffect(() => {
getList();
}, []);
console.log(list);
return (
<Fragment>
{" "}
<h1 class="text-center">Employee Skills Tracker</h1>
<table class="table mt-5 text-center">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th></th>
<th></th>
<th>Skill(s)</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{list.map(item => (
<tr key={item.employee_uuid}>
<td>{item.firstname}</td>
<td>{item.lastname}</td>
<td>Edit</td>
<td><button className="btn btn-danger" onClick={() => deleteEmployee(item.employee_uuid)}>Delete</button></td>
<td>{item.summary}</td>
<td>Edit</td>
<td><button className="btn btn-danger" onClick={() => deleteSkill(item.employee_uuid)} >Delete</button></td>
</tr>
))}
</tbody>
</table>
</Fragment>
);
};
export default List;

Instead of using list.filter(), use list.map(). The key is that you want to return a 1:1 list of modified elements, not a list without certain elements.
If you want to remove the summary for just a specific employee, I'd recommend
setList(list.map(item => {
if (item.id !== id) {
return item; // not the right employee, pass it through
}
let newItem = {...item};
delete newItem.summary;
return newItem;
}));
It's more complex, because you have to explicitly return what you want, but it's also what you're asking for.

Related

Why when I map a fetched array with ReactJs it only displays one result, when it brings all the results?

I'm fetching data from an endpoint, and if I use a console.log(data) it shows it correctly.
Image of my array
I only want data.data, so I use console.log(data.data) and apparently It show correctly what I need.
Data that I need
My problem is that I'm trying to show all clients in a table, but it only shows one of them.
This is my code:
import { useEffect, useState } from "react";
function Activity() {
function LoadActivity() {
fetch("https://localhost:7079/client/listClient")
.then((response) => {
return response.json()
})
.then((data) => {
setActivity([data.data])
});
}
const [actividad, setActivity] = useState([]);
useEffect(() => {
LoadActivity();
}, []);
return (
<div>
<h1>Cliente</h1>
<table>
<thead>
<tr>
<th>Id</th>
<th>name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{
actividad.map((client, i )=>(
<tr key={client[i].id}>
<td>{client[i].name}</td>
<td>{client[i].Email}</td>
<td>{client[i].id}</td>
</tr>
))
}
</tbody>
</table>
</div>
);
}
export default Activity;
And this is how it displays:
What is displayed
I tried to do it like this question 3 years ago.
I'm very new with React, and I don't know what I'm missing.
This is how I resolved it
const [equipo, setEquipo] = useState([]);
useEffect(() => {
obtenerDatos();
}, []);
const obtenerDatos = async () =>{
const datos = await fetch('https://localhost:7079/cliente/listarClientes')
const clientes = await datos.json()
setEquipo(clientes.data)
}

Log one field from each element of an array

I have objects, in the database. I want to grab the userId using axios, but when I tried to console.log() it. It shows undefined. hen I hardcoded it and targeted it by array, it shows.
How can I console log all of userId? I would like to grab it so I can use it as an endpoint for my database
const res = await userRequest.get('user/find/'+userId)
I want to grab the userId only.
import React, { useEffect, useState } from 'react'
import { format } from 'timeago.js'
import { userRequest } from '../../requestMethod'
import './Widgetlg.css'
const WidgetLg = () => {
const Button = ({ type }) => {
return <button className={'widgetLgButton ' + type}>{type}</button>
}
const [orders, setOrders] = useState([])
const [users, setUsers] = useState([])
useEffect(() => {
const getOrders = async () => {
//this is just a shorcut api
try {
const res = await userRequest.get('orders')
setOrders(res.data)
console.log(res.data?.userId)
console.log(res.data)
console.log(res.data[0].userId)
} catch (error) {
console.log(error)
}
}
getOrders()
}, [])
useEffect(() => {
const getUsername = async () => {
try {
const res = await userRequest.get('user/find/')
setUsers(res.data)
} catch (error) {}
}
getUsername()
}, [])
return (
<div className="widgetLg">
<h3 className="widgetLgTitle">Latest Transactions</h3>
<table className="widgetTable">
<tr className="widgetLgTr">
<th className="widgetLgTh">Customer</th>
<th className="widgetLgTh">Date</th>
<th className="widgetLgTh">Amount</th>
<th className="widgetLgTh">Status</th>
</tr>
{orders.map((order) => (
<tr className="widgetLgTr">
<td className="widgetLgUser">
<span className="WidgetLgName"> **I want here to show the username** </span>
</td>
<td className="widgetLgDate"> {format(order.createdAt)} </td>
<td className="widgetLgAmmount">P {order.amount} </td>
<td className="widgetLgStatus">
<Button type={order.status} />
</td>
</tr>
))}
</table>
</div>
)
}
export default WidgetLg
You could try something like this if I understand you correctly
const userIdsArray = res.data.map(d => d.userId);
console.log(userIdsArray);
res.data is an array. To log all elements, you could just iterate over them:
res.data.forEach(el => console.log(el.userId));
The reason that console.log(res.data) gives undefined is that the array itself doesn't have a userId field, only the elements of the array do.

React toggle "Show All" and "Show Winners" doesn't work

I have been struggling on this piece of codes which I suppose a button's clicked, the table will toggle between show all items and show winner items only.
Problem: The button has to be clicked two times to show winner items. Can't revert back to show all.
Do appreciate if someone can help. Thank you so much.
const MovieList = () => {
// Get Movies
const [movies, setMovies] = useState([])
const [winner, filterWinner] = useState(false)
const fetchMovies = async () => {
const res = await fetch('http://localhost:5000/data')
const data = await res.json()
return data
}
useEffect(() => {
const getMovies = async () => {
const moviesFromServer = await fetchMovies()
setMovies(moviesFromServer)
}
getMovies()
}, [])
//toggle between setting movies to all movies and winner movies.
//movie is an object that has a key and value pair "winner" : "True" or "winner" : "False"
const toggleWinner = () => {
filterWinner(!winner)
if (winner === true) {
const winners = movies.filter((movie) => movie.winner === 'True');
setMovies(winners);
} else {
setMovies(movies);
}
}
return (
<div className="container">
<h1>Movies</h1>
<hr />
<div>
<Button onClick={toggleWinner} color="info">{winner ? "Show All" : "Show Winners"}</Button>
</div>
<div>
<table className="table table-bordered table-striped">
<thead className="thead-dark">
<tr>
<th>Year</th>
<th>Film Name</th>
<th>Oscar Winner</th>
<th>Country</th>
</tr>
</thead>
<tbody>
{movies.map(movie => (
<tr key={movie.id}>
<td>{movie.year}</td>
<td>{movie.filmName}</td>
<td>{movie.winner}</td>
<td>{movie.country}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)
}
export default MovieList;
The problem here is when you set state, that state will not be updated immediately, so you can't compare winner to true.
You can try this approach
const toggleWinner = () => {
//winner = false
filterWinner(prev => {
if(!prev) { // winner = true
const winners = movies.filter((movie) => movie.winner === "True");
setMovies(winners);
}
else {
setMovies(movies);
}
return !prev
});
};
Another problem is that you mutated the movies, so when you toggle again, old movies value is gone.
Check this codesandbox to see how I fixed that: https://codesandbox.io/s/frosty-browser-o8jeb?file=/src/App.js
If your state change depends on previous state value you need to call a function inside your update function.
const [state,setState]=useState(false);
If you want to toggle the state value you need to call update function like this.
setState(state=>!state)
In your case
filterWinner(winner=>!winner)
Note: you can use any name you want as argument inside update function.

TypeError: cars.map is not a function - ReactJs

I'm trying to get all data into a table and I'm using reactjs and hooks to implement CRUD so I gott this error please can someone help me to fix this issue.
Here is the code :
const [car, setCarh] = useState(initialState)
useEffect(()=> {
retrieveCars();
}, [])
const retrieveCars =() =>{
DataService.getAll()
.then(response => {
setCarh(response.data)
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
<tbody>
{
cars.map((data) => (
<tr >
<th scope="row">{data.idCars}</th>
<td>{data.carName}</td>
<td>{data.carModel}</td>
<td>
<Link to={"/classement/update/" + data.idCars}
className="btn btn-info"
>
Edit
</Link>
initially, you define cars as an object:
initialState = { idCars: null, carName: "", carModel: "" }
objects don't have a map method.
if you are going to show data of more than one car, you need to create an array and fill it with objects, each representing a car.
if you want to show data of a single car, you don't need to use map. for example:
const [car, setCar] = useState(initialState)
return (<tr >
<th scope="row">{car.carName}</th>
<td>{data.carModel}</td>
<td></tr> ....

How to delete data from database by using React js and REST API

I am learning REST API. I am using the react app for front end and backend for Node js and express server. For API I am using REST API. I am using MongoDB for the database. I successfully display all the data to the browser. I can able to search the data. Now I want to delete the data. I don't know how to delete data from REST API endpoint. I will be really glad if someone help me out. I tested my backend by using Postman. Everything works fine as expected.
This is my backend delete end point
app.delete("/students/:id", async (req, res, next) => {
const id = req.params.id;
try {
student
.remove({ _id: id })
.exec()
.then(data => {
res.json(data);
});
} catch (error) {
console.log(error);
}
});
I export my API END points to React js
export async function deleteStudent(id) {
const response = await fetch(`/students/${id}`, {
method: "DELETE"
});
return response.json();
}
This is the main component where I want to delete the data
import React, { useState, useEffect } from "react";
import { logEntry } from "../Api/Api";
import { deleteStudent } from "../Api/Api";
function Datatable() {
const [total, settotal] = useState([]);
const [searchItem, setsearchItem] = useState({
item: ""
});
const [data, setdata] = useState([]);
const handleChange = e => {
setsearchItem({ item: e.target.value });
};
const getEntries = async () => {
const logEntries = await logEntry();
console.log(logEntries);
settotal(logEntries.count);
setdata(logEntries.students);
};
const nameFilter = data.filter(list => {
return list.name.toLowerCase().includes(searchItem.item.toLowerCase());
});
const deleteData = async id => {
await deleteStudent(id);
};
useEffect(() => {
getEntries();
}, []);
return (
<div>
<div style={{ paddingLeft: "800px" }}>
<input
placeholder="Search student"
onChange={handleChange}
style={{ width: "200px", height: "30px" }}
/>
</div>
<p>Total student: {total} </p>
<table>
<thead>
<tr>
<th>Name</th>
<th>City</th>
<th>Address</th>
<th>Phone</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{nameFilter === "" ? (
<p>Student not found</p>
) : (
nameFilter.map(list => {
return (
<tr>
<td>{list.name}</td>
<td>{list.city}</td>
<td>{list.address}</td>
<td>{list.phone}</td>
<td>{list.email}</td>
<td>
<a
className="waves-effect red btn-small"
onClick={() => deleteData(list.id)}
>
Delete
</a>
</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
);
}
export default Datatable;
I don't know, Am I doing?
This looks like a great start! I'm operating on the understanding that you need to somehow pass the id of the student you want to delete into the URL in deleteStudent() from the 'DELETE' button in your <DataTable> component.
So, first, let's refactor your deleteStudent() function:
export async function deleteStudent(id) {
const response = await fetch(`/students/${id}`, {
method: "DELETE",
});
return response.json();
}
You don't need to send any data with a DELETE request, you just need to hit the correct URL based on the id, which we can pass in to the method and dynamically include in the fetch() call.
Now, you need to find some way to pass that id into the deleteStudent() function. From what I can see, you are pulling in the student data here (I've paraphrased this):
const getEntries = async () => {
// students are pulled in, I'm assuming they have an 'id' property that corresponds to the 'id' that MongoDB has them stored under
const logEntries = await logEntry();
// and now data references the students
setdata(logEntries.students);
};
It looks like then you filter the students here:
const nameFilter = data.filter(list => {
return list.name.toLowerCase().includes(searchItem.item.toLowerCase());
});
And then render the filtered students with a call to .map(). This is where you can pass the id along in the onClick handler, assuming that you DO have an id field on these list elements. If you don't, then you will need to find a way to add the id in to this data:
nameFilter.map(list => {
return (
<tr>
<td>{list.name}</td>
<td>{list.city}</td>
<td>{list.address}</td>
<td>{list.phone}</td>
<td>{list.email}</td>
<td>
<a
className="waves-effect red btn-small"
onClick={() => deleteData(list.id)} // this is where the id should get passed on to the handler, and then dynamically included in the DELETE /students/:id url
>
Delete
</a>
</td>
</tr>
);
})
Then, in your deleteData() function, you will receive the id as a param, and you can call your deleteStudent(id) function to make the request to the backend:
const deleteData = async id => {
await deleteStudent(id);
};
There are some other things that need work, but you have the general idea correct! I'll give some hints towards further improvements below.
Do these need to be separate, or can they be combined?
import { logEntry } from "../Api/Api";
import { deleteStudent } from "../Api/Api";
Maybe clean up the DELETE route-handler:
app.delete("/students/:id", async (req, res, next) => {
const id = req.params.id;
try {
// generally, Mongoose Model's are represented with TitleCase
Student
.remove({ _id: id })
.exec() // is this needed? https://mongoosejs.com/docs/api/model.html#model_Model-remove
.then(data => {
res.json(data);
});
} catch (error) {
console.log(error);
}
});
There seems to be some extra state/hooks lying around in this Datatable:
function Datatable() {
// ... bunch of code
// do you need state for this?
const [removeStudent, setremoveStudent] = useState([]);
// ... more code
const getEntries = async () => {
// ...
setremoveStudent(deleteData); // not sure this is needed...
};

Resources