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]);
Related
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 (
...
);
};
at the first render everything is ok, but when i refresh or reload the page fetch doesn't work, in the console i get an empty array. why there is no data after refreshing the page. i tried with async function. it doesn't work. in stack overflow, there are many solutions near my problem. but no one worked for me. is there anyone to help, how can i solve this?
import { useEffect, useState } from 'react';
import { CircularProgress } from '#mui/material';
import useAuth from '../../hooks/useAuth';
import './MyOrders.css';
const MyOrders = () => {
const [packages, setPackages] = useState([]);
const {user} = useAuth();
//fetch Data
useEffect(() => {
async function fetchData(){
await fetch(`http://localhost:4000/orderedItem?email=${user.email}`)
.then(res => res.json())
.then(data => setPackages(data))
}
fetchData()
}, [])
//delete an order
const handleDeleteOrder = id => {
const confirmation = window.confirm('Are you sure, you want to delete this order?')
if(confirmation){
const url = `http://localhost:4000/orderedItem/${id}`;
fetch(url, {
method: 'delete'
})
.then(res => res.json())
.then(data => {
if(data.deletedCount > 0){
alert('deleted successfully')
const remainingProducts = packages.filter(packages => packages._id !== id);
setPackages(remainingProducts);
}
})
}
}
return (
<div className='my-orders'>
<h2>My Orders</h2>
{!packages.length ?
<CircularProgress style={{marginTop: '10%'}}/> :
<table style={{margin: '0 auto', borderCollapse: 'collapse', width: '90%'}}>
<thead>
<tr>
<th>Name</th>
<th>Place</th>
<th>Version</th>
<th>Price</th>
<th>Delete</th>
</tr>
</thead>
{packages.map(packages =>
<tr>
<td>{packages.productName}</td>
<td>{packages.place}</td>
<td>{packages.versionYear}</td>
<td>${packages.discountPrice}</td>
<td>
<p style={{border: '1px solid #F4D03F', borderRadius: '5px', color: '#D4AC0D',background: '#FEF9E7',padding: '0 3px', margin: '0', display: 'inline'}} onClick={() => handleDeleteOrder(packages._id)}>Cancel Order</p>
</td>
</tr>
)}
</table>
}
</div>
);
};
export default MyOrders;
Is the api call dispatched as expected? Can you see it in the network tab?
If it is dispatches, is the response correct? What does the data here -> .then(data => setPackages(data)) contain (try to console log it).
I am a bit suspicious if the problem is not in the es5 function. Try to change the fetchData to arrow function () => {}
I got the State Data from Store. I created the Search Box to filter that Data, Now I got the FilterData also, But how I need to update my UI with that Filtered Data, In HandleSearch method I stored the the Filtered data in FilteredData varibale, But I am Unable to Iterate the FilteredData varibale and I am unable to update in the UI, BUt it is working in console, Now i need to update in the UI, Please can anyone help in this, Thanks in Advance...
import { Dispatch } from "redux"
import axios from 'axios'
export const FETCH_SUCCESS : string ='FETCH_SUCCESS';
export const FETCH_SEARCH ='FETCH_SEARCH';
export const fetchUser=()=>{
return async (dispatch:Dispatch) =>{
try{
let dataUrl : string ="http://localhost:3000/users";
let response = await axios.get(dataUrl);
dispatch({type:FETCH_SUCCESS, payload : response.data})
} catch {
}
}
}
import * as searchAction from './SearchAction';
import {SearchingInter} from '../../componets/SearchingInter';
export interface ISearch{
search : SearchingInter[]
}
let initialSate : ISearch ={
search : [] as SearchingInter[]
}
export const reducer =(state =initialSate , action:any) :ISearch =>{
switch(action.type){
case searchAction.FETCH_SUCCESS :
return {
...state,
search : action.payload
};
default : return state;
}
}
import React, { ChangeEvent } from 'react';
import {useEffect} from 'react';
import {useSelector,useDispatch} from 'react-redux';
import * as searchActions from '../Redux/SearchFetch/SearchAction';
import * as searchReducsers from '../Redux/SearchFetch/Searchreducer';
import SearchingData from './SearchingData';
const Search = () => {
let dispatch = useDispatch();
let readingStateData : searchReducsers.ISearch = useSelector((state : {searchingData:searchReducsers.ISearch})=>{
return state.searchingData;
})
useEffect(() => {
console.log(readingStateData.search)
dispatch(searchActions.fetchUser());
}, [])
const handlesearching =(e:ChangeEvent<HTMLInputElement>)=>{
//console.log(e.target.value);
let defaultData = readingStateData.search;
//console.log(defaultData);
const filteredData = e.target.value ? defaultData.filter(user =>user.UserName.toLowerCase().startsWith(e.target.value)) : defaultData
}
return (
<React.Fragment>
<div className="container mt-3">
<div className="row">
<div className="col-md-3">
<div className="card">
</div>
</div>
</div>
</div>
<SearchingData handleSearch={handlesearching}/>
<table className="table table-hover text-center table-primary">
<thead className="text-black">
<tr>
<th>UserName</th>
<th>Phone</th>
<th>Email</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
<React.Fragment>
{
readingStateData.search.map(user =>{
return(
<tr>
<td>{user.UserName}</td>
<td>{user.PhoneNumber}</td>
<td>{user.email}</td>
<td>{user.gender}</td>
</tr>
)
})
}
</React.Fragment>
</tbody>
</table>
</React.Fragment>
)
}
export default Search;
import { type } from 'os';
import React, { ChangeEvent } from 'react'
type searchData = {
handleSearch : (e:ChangeEvent<HTMLInputElement>) => void;
}
const SearchingData:React.FC<searchData> = ({handleSearch}) => {
const UpdateData =(e:ChangeEvent<HTMLInputElement>)=>{
//console.log(e);
handleSearch(e)
}
return (
<React.Fragment>
<div>
<input type="text" onChange={UpdateData} />
</div>
</React.Fragment>
)
}
export default SearchingData
You need to have a state which will hold the filterData value. And set the initialValue of the state to the Data from the store
const [ dataToDisplay, setDataToDisplay ] = useState(readingStateData?.search || []);
Add a second useEffect which looks for the change in the readingStateData?.search. Initially you have the search as [] but once there is data we need to sync that data with the components's internal state.
useEffect(() => {
if(readingStateData?.search?.length > 0){
setDataToDisplay(readingStateData?.search)
}
}, [readingStateData?.search])
Now inside your handleChange you can update the state
const handlesearching =(e:ChangeEvent<HTMLInputElement>)=>{
const newDataToDisplay = e.target.value ? dataToDisplay.filter(user =>user.UserName.toLowerCase().startsWith(e.target.value)) : readingStateData?.search
setDataToDisplay(newDataToDisplay);
}
Now while rendering map over this dataToDisplay instead readingStateData?.search
dataToDisplay.map((user) => {
return (
<tr>
<td>{user.UserName}</td>
<td>{user.PhoneNumber}</td>
<td>{user.email}</td>
<td>{user.gender}</td>
</tr>
);
});
you can make your input as controlled input and have its value being read from the state
const Search = () => {
let dispatch = useDispatch();
let readingStateData: searchReducsers.ISearch = useSelector(
(state: {searchingData: searchReducsers.ISearch}) => {
return state.searchingData;
}
);
const [searchText, setSearchText] = useState('');
useEffect(() => {
console.log(readingStateData.search);
dispatch(searchActions.fetchUser());
}, []);
const handlesearching = (e: ChangeEvent<HTMLInputElement>) => {
setSearchText(e.target.value);
};
const dataToDisplay = searchText.trim().length > 0
? readingStateData?.search.filter((user) =>
user.UserName.toLowerCase().startsWith(searchText)
)
: readingStateData?.search;
return (
<React.Fragment>
<SearchingData handleSearch={handlesearching} searchText={searchText} />
{dataToDisplay.map((user) => {
return (
....
);
})}
</React.Fragment>
);
};
// In your Search Component add another prop called searchText
type searchData = {
handleSearch : (e:ChangeEvent<HTMLInputElement>) => void;
searchText: string;
}
const SearchingData:React.FC<searchData> = ({handleSearch, searchText}) => {
const UpdateData =(e:ChangeEvent<HTMLInputElement>)=>{
//console.log(e);
handleSearch(e)
}
return (
<React.Fragment>
<div>
<input type="text" value={searchText} onChange={UpdateData} />
</div>
</React.Fragment>
)
}
export default SearchingData
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
}