How to use React hooks to show a only updated data in table ?
Now I can display all the information. But I want to display the data in a table. When the data in firestore is updated, the data will be displayed on the website. so how to use a React Hook on this code to show information to display only a updated data ?
import React, { useEffect, useState } from "react";
import { Button } from "react-bootstrap";
import Table from 'react-bootstrap/Table';
import BookDataService from "../../services/book.services";
import "./table.css";
const tableList = () => {
const [profile, setBooks] = useState([]);
useEffect(() => {
getBooks();
}, []);
const getBooks = async () => {
const data = await BookDataService.getAllBooks();
console.log(data.docs);
setBooks(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
return (
<>
<div className="mb-4">
<Button variant="success edit" onClick={getBooks}>
Refresh
</Button>
</div>
{/* <pre>{JSON.stringify(books, undefined, 2)}</pre>} */}
<Table striped bordered hover variant="dark" size="md">
<thead>
<tr>
<th>RollNo</th>
<th>Name</th>
<th>Student ID</th>
<th>Subject</th>
<th>Room</th>
<th>Date</th>
<th>Time</th>
{/*<th>Action</th>*/}
</tr>
</thead>
<tbody>
{profile.map((doc, index) => {
const timestamp = { nanoseconds: doc.timeStamp.nanoseconds, seconds: doc.timeStamp.seconds }
const firebasetime = new Date(
timestamp.seconds * 1000 + timestamp.nanoseconds / 1000000,
);
const date = firebasetime.toDateString();
const timeStamp = firebasetime.toLocaleTimeString();
console.log(date, timeStamp);
return (
<tr key={doc.id}>
<td>{index + 1}</td>
<td>{doc.name}</td>
<td>{doc.sid}</td>
<td>{doc.Subject}</td>
<td>{doc.Room}</td>
<td>{date}</td>
<td>{timeStamp}</td>
</tr>
);
})}
</tbody>
</Table>
</>
);
};
export default tableList;
Here is the simple example for show only updated data:
import { useState, useEffect } from 'react';
const SimpleTable = () => {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://samplejsonholder.test.com/test');
const data = await response.json();
setData(data);
//You can replace this with data source or api call
};
fetchData();
}, []);
useEffect(() => {
// Every time the 'data' state updates, this will run.
console.log('data updated:', data);
}, [data]);
return (
...
);
};
Related
I am doing a project in which I need to show all the product information from the MongoDB database in UI. This data will be shown in UI in tabular form. So I want to use react table. In each row, there will be a button to delete that product information from the UI and also from MongoDB. I am trying to pass the id to button but every time I am getting undefined. value. How can I pass MongoDB product id correctly to react table?
though I am doing it in the following way. But I really want to do it in react table.
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import { useAuthState } from 'react-firebase-hooks/auth';
import auth from '../../firebase.init';
import './Table.css'
const Table = () => {
const [data, setData] = useState([])
const [user] = useAuthState(auth)
const email = user.email
useEffect(() => {
fetch(`https://polar-castle-01342.herokuapp.com/inventory`)
.then((response) => response.json())
.then((data) => setData(data))
}, [])
const handleDeleteButton = async id => {
const proceed = window.confirm('Are you sure you want to delete this product?')
if (proceed) {
const url = `https://polar-castle-01342.herokuapp.com/inventory/${id}`
const response = await axios.delete(url)
const remaining = data.filter(item => item._id !== id)
setData(remaining)
}
}
return (
<div className='mt-5 table-responsive'>
<h1 className='text-center mb-3'>Inventory Products</h1>
<table border={1} cellPadding={5} className='w-100'>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Supplier Name</th>
<th>Options</th>
</tr>
</thead>
<tbody>
{data.map(product => (
<tr key={product._id}>
<td>{product._id}</td>
<td>{product.name}</td>
<td>{product.price}</td>
<td>{product.quantity}</td>
<td>{product.supplierName}</td>
<td><button onClick={() => handleDeleteButton(product._id)} className='delete-button'>Delete</button></td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default Table;
You should write like this
`const handleDeleteButton = async id => {
const proceed = window.confirm('Are you sure you want to delete this product?')
if (proceed) {
const url = `https://polar-castle-01342.herokuapp.com/inventory/${id}`
const response = await axios.delete(url)
const remaining = data.filter(item => item._id !== id)
setData(remaining)
}
}and useEffect(() => {
fetch(https://polar-castle-01342.herokuapp.com/inventory)
.then((response) => response.json())
.then((data) => setData(data))
}, [handleDeleteButton])`
I hope it'll useful
import { useState, useEffect } from 'react';
import axios from 'axios';
export const GetData = (url) => {
const [data, setData] = useState([]);
useEffect(() => {
const getData = async () => {
try {
const response = await axios.get(url);
setData(response.data);
} catch (error) {
console.error(error);
}
};
getData();
return () => {};
}, [data]);
return {
data,
};
};
I am using the component above for multiple multiple components. My problem is that the request keeps repeating over and over again. I want to stop it from repeating. The only time it should repeat is when "data" changes.
I tried using axios' cancel token but that did not work. I also tried stopping it using intervals (maybe I did it incorrectly. I don't know). The code below shows one of the components that I am using the GetData component. Hopefully this helps.
import React from 'react';
import { Card, Table, Button } from 'react-bootstrap';
import { DeleteData, GetData } from '../../../api';
import { useGlobalContext } from '../../../context';
const SectionTable = ({ url }) => {
const { setIsEdit, getId } = useGlobalContext();
const { data } = GetData(url);
return (
<>
<Card style={{ maxHeight: '500px', overflowY: 'scroll' }}>
<Card.Body>
<Table striped responsive hover>
<thead>
<th>Section Level</th>
<th>Section Name</th>
<th></th>
<th></th>
</thead>
<tbody>
{data.map((section) => {
return (
<tr key={section.id}>
<td>{section.section_level}</td>
<td>{section.section_name}</td>
<td>
<Button
variant='warning'
onClick={() => {
getId(section.id);
setIsEdit(true);
}}>
Edit
</Button>
</td>
<td>
<Button
variant='danger'
onClick={() => DeleteData(section.id)}>
Delete
</Button>
</td>
</tr>
);
})}
</tbody>
</Table>
</Card.Body>
</Card>
</>
);
};
export default SectionTable;
```**strong text**
listing data as a dependency means that the effect runs after every render that was triggered by a change in data. Instead depend on url:
useEffect(() => {
const getData = async () => {
try {
const response = await axios.get(url);
setData(response.data);
} catch (error) {
console.error(error);
}
};
getData();
return () => {};
}, [url]);
I am trying to build a search and sorting functionality for the table content. I don't want to use package as I am trying to learn and see how the react search work. I have the following that loads the content from payloads
import React, {useState, useEffect} from 'react'
import '../css/about.css';
import Pagination from '../components/Pagination'
function About() {
const [userData, setUserData] = useState([]);
const [loading , setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [postsPerPage, setPostsPerPage] = useState(5);
const [search, setSearch] = useState("");
async function getData()
{
let response = await fetch('https://api.github.com/users');
let data = await response.json();
// setUserData(data)
return data;
}
//call getData function
getData()
.then(data => console.log(data)
);//
useEffect(() => {
setLoading(true)
getData()
.then(
data => {
setUserData(data) }
)
.catch(error => {
console.log(error);
})
}, [])
// Get current posts
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage;
const currentPosts = userData.slice(indexOfFirstPost, indexOfLastPost);
// changw page
const paginate = (pageNumber) => setCurrentPage(pageNumber);
// Search Table
const handleFilterChange = e => {
const value = e.target.value || undefined;
if( search !== "" && userData.login.indexOf(search.toLowerCase()) === -1 ) {
return null;
}
setSearch(value)
}
return (
<div className="container">
<div>
<input value={search}
onChange={handleFilterChange}
placeholder={"Search"}
/>
<table>
<thead>
<tr>
<td>id</td>
<td>avatar_url</td>
<td>events_url</td>
<td>followers_url</td>
<td>following_url</td>
<td>gists_url</td>
<td>gravatar_id</td>
<td>html_url</td>
<td>login</td>
<td>node_id</td>
<td>organizations_url</td>
<td>received_events_url</td>
<td>repos_url</td>
<td>site_admin</td>
<td>starred_url</td>
<td>subscriptions_url</td>
<td>type</td>
<td>url</td>
</tr>
</thead>
<tbody>
{
currentPosts.map((item, index) => (
<tr key={index}>
<td>{item.id}</td>
<td>{item.avatar_url}</td>
<td>{item.events_url}</td>
<td>{item.followers_url}</td>
<td>{item.following_url}</td>
<td>{item.gists_url}</td>
<td>{item.gravatar_id}</td>
<td>{item.html_url}</td>
<td>{item.login}</td>
<td>{item.node_id}</td>
<td>{item.organizations_url}</td>
<td>{item.received_events_url}</td>
<td>{item.repos_url}</td>
<td>{item.site_admin}</td>
<td>{item.starred_url}</td>
<td>{item.subscriptions_url}</td>
<td>{item.type}</td>
<td>{item.url}</td>
</tr>
))
}
</tbody>
</table>
<Pagination postsPerPage={postsPerPage} totalPosts={userData.length} paginate={paginate} />
</div>
</div>
)
}
export default About
The pagination code is listed below.
import React from 'react'
const Pagination = ({ postsPerPage, totalPosts, paginate }) => {
const pageNumbers = [];
for(let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++) {
pageNumbers.push(i);
}
return (
<div>
<ul className="pagination">
{pageNumbers.map(number => (
<li key={number} className="page-item">
<a onClick={() => paginate(number)}
href="#" className="page-link">
{number}
</a>
</li>
))}
</ul>
</div>
)
}
export default Pagination
I am think because I used .map within the tbody and the search isn't affecting the content. Though I have no error, only that nothing is displaying from search parameters.
I noticed you didn't create the function to handle the searching. You can use this generic approach which will search across the rows and the column and will match the cases.
function DataSearch(rows) {
const columns = rows[0] && Object.keys(rows[0]);
return rows.filter((row) =>
columns.some((column) => row[column].toString().toLowerCase().indexOf(search.toLowerCase()) > -1)
);
}
instantiate the function
const searchPosts = DataSearch(currentPosts);
Use the searchPosts on your .map function in tbody.
I am working on a React project, In that project I am trying to sorting. In my component I have
Two buttons. The first button is Change to Min and the second button is Change to Max.
And in the same component I am showing the data that is coming from the backend.
Now If I click the button the sorting logic state has to apply to the data what I am showing by
Using the map method.
This is list.js
import React, { useState, useEffect } from 'react';
import { Table, Button } from 'reactstrap';
import Aumservice from '../../service/aum-service';
import { MdEdit } from 'react-icons/md';
import { IconContext } from "react-icons";
const List = (props) => {
const [sortData, setSortData] = useState(null)
const [data, setData] = useState([])
useEffect(() => {
(async function () {
const response = await Aumservice.getAum()
const dataResponse = response.data.list.map(ele => ele.maxValue)
setSortData(dataResponse)
setData(response.data.list)
})()
}, [])
const sortAscending = () => {
let sortedData = sortData.sort((a, b) => a - b)
console.log(sortedData)
setData(sortedData)
}
const sortDescending = () => {
let sortedData = sortData.sort((a, b) => b - a)
setData(sortedData)
}
return (
<div>
<IconContext.Provider
value={{ size: '25px' }}
>
<Table bordered>
<thead>
<tr>
<th>So No</th>
<th>Min</th>
<th>Max</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{setData.map((currentValue, index) => {
return < tr key={index + 1} >
<th scope="row">{index + 1}</th>
<td>{currentValue.minValue}</td>
<td>{currentValue.maxValue}</td>
</tr>
})}
</tbody>
</Table>
</IconContext.Provider>
<div className='min pr-5'>
<Button onClick={sortAscending} className='primary'>Change to Min</Button>
</div>
<div className='max'>
<Button className='secondary'>Change to Max</Button>
</div>
</div >
)
}
export default List
If I am not clear with my doubt please put a comment.
If I get it right, you want your data to be sorted by maxValue, in a way that depends on which button is clicked (ascending/descending).
There is a typo in the mapping element, instead of setData.map((.. you need data.map((...
An onClick event must be added at the second button with the sortDescending function.
You do not need a second variable sortData for sorting your data, you can sort the existing list that you get from the response.
According to the above conclusions, I have edited your code:
import React, { useState, useEffect } from 'react';
import { Table, Button } from 'reactstrap';
import Aumservice from '../../service/aum-service';
import { MdEdit } from 'react-icons/md';
import { IconContext } from "react-icons";
const List = (props) => {
const [data, setData] = useState([])
useEffect(() => {
(async function () {
const response = await Aumservice.getAum()
setData(response.data.list)
})()
}, [])
const sortAscending = () => {
let copyData = JSON.parse(JSON.stringify(data));
// If you want to sort by minValue,just change accordingly the below properties
let sortedData = copyData.sort((a, b) => (a.maxValue > b.maxValue) ? 1 : -1);
console.log(sortedData)
setData(sortedData)
}
const sortDescending = () => {
let copyData = JSON.parse(JSON.stringify(data));
// If you want to sort by minValue,just change accordingly the below properties
let sortedData = copyData.sort((a, b) => (a.maxValue < b.maxValue) ? 1 : -1);
console.log(sortedData)
setData(sortedData)
}
return (
<div>
<IconContext.Provider
value={{ size: '25px' }}
>
<Table bordered>
<thead>
<tr>
<th>So No</th>
<th>Min</th>
<th>Max</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{data.map((currentValue, index) => {
return <tr key={index + 1} >
<th scope="row">{index + 1}</th>
<td>{currentValue.minValue}</td>
<td>{currentValue.maxValue}</td>
</tr>
})}
</tbody>
</Table>
</IconContext.Provider>
<div className='min pr-5'>
<Button onClick={sortAscending} className='primary'>Change to Min</Button>
</div>
<div className='max'>
<Button onClick={sortDescending} className='secondary'>Change to Max</Button>
</div>
</div >
)
}
export default List
The delete function of my app is working fine, however it requires the user to manually refresh the page after the user click the delete button in order to see the new list of elements in my database. I would like to automatically refresh after the click event. I am using react hooks for this projects. However, I found one solution if I remove useEffect's [] but in my backend it shows, its requesting crazily. I don't know, is it wise to remove useffect's [ ]?
Here is the component where it fetches data from backend and passes the props to another component
import React, { useState, useEffect } from "react";
import axios from "axios";
import Table from "../Table/Table";
import "./Display.css";
const Display = () => {
const [state, setState] = useState({ students: [], count: "" });
const [searchItem, setsearchItem] = useState({
item: ""
});
const Search = e => {
setsearchItem({ item: e.target.value });
};
useEffect(() => {
axios
.get("/students")
.then(response => {
setState({
students: response.data.students,
count: response.data.count
});
})
.catch(function(error) {
console.log(error);
});
}, []); //If I remove this square brackets, it works
const nameFilter = state.students.filter(list => {
return list.name.toLowerCase().includes(searchItem.item.toLowerCase());
});
return (
<div>
<h3 align="center">Student tables</h3>
<p align="center">Total students: {state.count}</p>
<div className="input-body">
<div className="row">
<div className="input-field col s6">
<input placeholder="search student" onChange={Search} />
</div>
</div>
</div>
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Date of birth</th>
<th>Address</th>
<th>Zipcode</th>
<th>City</th>
<th>Phone</th>
<th>Email</th>
<th colSpan="2">Action</th>
</tr>
</thead>
{nameFilter.map((object, index) => {
return (
<tbody key={index}>
<Table obj={object} /> //In here I am passing the props to the another component.
</tbody>
);
})}
</table>
</div>
);
};
export default Display;
This is second component which receives the props.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
const Table = props => {
const removeData = () => {
axios
.delete("/students/" + props.obj.id)
.then(console.log("Deleted"))
.catch(err => console.log(err));
};
return (
<React.Fragment>
<tr>
<td>{props.obj.name}</td>
<td>{props.obj.birthday}</td>
<td>{props.obj.address}</td>
<td>{props.obj.zipcode}</td>
<td>{props.obj.city}</td>
<td>{props.obj.phone}</td>
<td>{props.obj.email}</td>
<td>
<Link
to={"/edit/" + props.obj.id}
className="waves-effect waves-light btn"
>
Edit
</Link>
</td>
<td>
<button onClick={removeData} className="waves-effect red btn ">
Remove
</button>
</td>
</tr>
</React.Fragment>
);
};
export default Table;
The [] in the useEffect hook is a dependency array to trigger the effect to run. If you want to trigger the effect (without it going off mercilessly), you can create a new variable that triggers that effect to run.
import React, { useState, useEffect } from "react";
import axios from "axios";
import Table from "../Table/Table";
import "./Display.css";
const Display = () => {
const [state, setState] = useState({ students: [], count: "" });
const [requestData, setRequestData] = useState(new Date());
const [searchItem, setsearchItem] = useState({
item: ""
});
const Search = e => {
setsearchItem({ item: e.target.value });
};
useEffect(() => {
axios
.get("/students")
.then(response => {
setState({
students: response.data.students,
count: response.data.count
});
})
.catch(function(error) {
console.log(error);
});
}, [requestData]);
const nameFilter = state.students.filter(list => {
return list.name.toLowerCase().includes(searchItem.item.toLowerCase());
});
return (
<div>
<h3 align="center">Student tables</h3>
<p align="center">Total students: {state.count}</p>
<div className="input-body">
<div className="row">
<div className="input-field col s6">
<input placeholder="search student" onChange={Search} />
</div>
</div>
</div>
<table className="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Date of birth</th>
<th>Address</th>
<th>Zipcode</th>
<th>City</th>
<th>Phone</th>
<th>Email</th>
<th colSpan="2">Action</th>
</tr>
</thead>
{nameFilter.map((object, index) => {
return (
<tbody key={index}>
<Table obj={object} setRequestData={setRequestData} />
</tbody>
);
})}
</table>
</div>
);
};
export default Display;
Then you can trigger it from your Table component:
import React, { useState } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
const Table = props => {
const removeData = () => {
axios
.delete("/students/" + props.obj.id)
.then(() => {
props.setRequestData(new Date());
})
.catch(err => console.log(err));
};
return (
<React.Fragment>
<tr>
<td>{props.obj.name}</td>
<td>{props.obj.birthday}</td>
<td>{props.obj.address}</td>
<td>{props.obj.zipcode}</td>
<td>{props.obj.city}</td>
<td>{props.obj.phone}</td>
<td>{props.obj.email}</td>
<td>
<Link
to={"/edit/" + props.obj.id}
className="waves-effect waves-light btn"
>
Edit
</Link>
</td>
<td>
<button onClick={removeData} className="waves-effect red btn ">
Remove
</button>
</td>
</tr>
</React.Fragment>
);
};
export default Table;
Not sure if helps but you can always remove the item from the current array, so a refresh is not needed, for example you can pass as props a function that receives an id and then filter the students array to exclude the element that matches with that id and then update the state with the new array and count properties, something like this
In your parent:
const Display = () => {
const [state, setState] = useState({ students: [], count: "" });
const deleteItem = (id) => {
const newStudents = state.students.filter(student => student.id !== id)
const newCount = newStudents.length;
setState({ students: newStudents, count: newCount })
}
// Rest of the code
}
Now pass that function to your child component.
<Table obj={object} deleteItem={deleteItem} />
In the child component just modify your removeData method to add the deleteItem prop:
const Table = props => {
const removeData = () => {
axios
.delete("/students/" + props.obj.id)
.then(console.log("Deleted"))
.catch(err => console.log(err));
// Now if your request succeeds call the function to remove the item from the students state array
props.deleteItem(props.obj.id);
};
// Rest of the code
}
I know this does not answer your question, but when you're working with react or it is better to do this computations and filters on the app side, like in this case that even though the record was deleted from the db we also removed the record from the student state object and there's no need to refresh the page.
Remember, you're creating a single page application, so we want the nicest experience for the user without refreshing the page for every action the user makes.
Have a look at this
import React, { useState } from "react";
const Display = () => {
const [refresh, setRefresh] = useState(false)
const delete=() => {
// ................. //delete logic
reload ? setRefresh(false) : setRefresh(true) //toggle just to change state
}
useEffect(() => {
}, [reload]); //inject as dependency
}