How to update Api without making an infinite loop? - javascript

I want to update and display Api value whenever new POST request is create, without refreshing the page.
Here is the problem, In my React hook implementation when I make an API call it create's an infinite Api call to the server. I just want it to make a call when new POST request is added.
What I'm doing wrong and How to fix it?
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = () => {
fetch('http://127.0.0.1:8000/main/api/')
.then(response => response.json())
.then((res) => {
const data = res.map((item, index) => ({
title: item.title,
price: item.price,
quantity: item.quantity,
}));
setData(data);
});
};
console.log(data)
fetchData();
},[data]);
Full Code App.js
export default function OrderPreview(props) {
const classes = useStyles();
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = () => {
fetch('http://127.0.0.1:8000/main/api/')
.then(response => response.json())
.then((res) => {
const data = res.map((item, index) => ({
title: item.title,
price: item.price,
quantity: item.quantity,
}));
setData(data);
});
};
console.log(data)
fetchData();
},[data]);
return (
<main className={clsx(classes.content, {
[classes.contentShift]: props.openstate, })}>
<Grid container >
<Grid item sm={12} >
<TableContainer className={classes.tableStyle}>
<Table>
<TableHead>
<TableRow>
<TableCell align='center' >#</TableCell>
<TableCell style={{paddingLeft:'90px'}} >Name</TableCell>
<TableCell align='center' >Quantity</TableCell>
<TableCell align='center' >Price</TableCell>
</TableRow>
</TableHead>
<TableBody >
{data.map((item, index) => (
<TableRow key={index}>
<TableCell align='center' >{index + 1}</TableCell>
<TableCell style={{paddingLeft:'90px'}} >{item.title}</TableCell>
<TableCell align='center' >{item.quantity}</TableCell>
<TableCell align='center' >$ {item.price * item.quantity}</TableCell>
</TableRow>
))}
<TableCell colSpan={3} align='right' style={{paddingRight:'20px'}}> Total Price</TableCell>
<TableCell align='center' > non </TableCell>
</TableBody>
</Table>
</TableContainer>
</Grid>
</Grid>
</main>
);
}

If you don't want to keep fetching API, just remove data in the dependency array.
You are having setData(data); inside your useEffect so it will just keep running re-render over again result in useEffect and setData(data).
useEffect(() => {
const fetchData = () => {
fetch("http://127.0.0.1:8000/main/api/")
.then((response) => response.json())
.then((res) => {
const data = res.map((item, index) => ({
title: item.title,
price: item.price,
quantity: item.quantity,
}));
setData(data);
});
};
console.log(data);
fetchData();
}, []); // <-- REMOVE DATA HERE

Related

Uncaught Error: Rendered more hooks than during the previous render. How to fix?

I need to fetch data from API based on key and place the data inside a tablecell. I have tried something like the following but didn't work. It is showing an uncaught error.In that case, I know hooks shouldn't be called inside loops, conditions, or nested functions. Then how I would get the item.id?
Uncaught Error: Rendered more hooks than during the previous render.
My code is:
import React, { useState, useEffect } from 'react';
import {
Table, TableRow, TableCell, TableHead, TableBody,
} from '#mui/material';
import makeStyles from '#mui/styles/makeStyles';
import { useEffectAsync } from '../reactHelper';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import SettingsMenu from './components/SettingsMenu';
import CollectionFab from './components/CollectionFab';
import CollectionActions from './components/CollectionActions';
import TableShimmer from '../common/components/TableShimmer';
const useStyles = makeStyles((theme) => ({
columnAction: {
width: '1%',
paddingRight: theme.spacing(1),
},
}));
const StoppagesPage = () => {
const classes = useStyles();
const t = useTranslation();
const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
const [geofences, setGeofences] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
fetch('/api/geofences')
.then((response) => response.json())
.then((data) => setGeofences(data))
.catch((error) => {
throw error;
});
}, []);
useEffectAsync(async () => {
setLoading(true);
try {
const response = await fetch('/api/stoppages');
if (response.ok) {
setItems(await response.json());
} else {
throw Error(await response.text());
}
} finally {
setLoading(false);
}
}, [timestamp]);
return (
<PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'settingsStoppages']}>
<Table>
<TableHead>
<TableRow>
<TableCell>{t('settingsStoppage')}</TableCell>
<TableCell>{t('settingsCoordinates')}</TableCell>
<TableCell>{t('sharedRoutes')}</TableCell>
<TableCell className={classes.columnAction} />
</TableRow>
</TableHead>
<TableBody>
{!loading ? items.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.name}</TableCell>
<TableCell>{`Latitude: ${item.latitude}, Longitude: ${item.longitude}`}</TableCell>
<TableCell>
{
geofences.map((geofence) => geofence.name).join(', ')
}
</TableCell>
<TableCell className={classes.columnAction} padding="none">
<CollectionActions itemId={item.id} editPath="/settings/stoppage" endpoint="stoppages" setTimestamp={setTimestamp} />
</TableCell>
</TableRow>
)) : (<TableShimmer columns={2} endAction />)}
</TableBody>
</Table>
<CollectionFab editPath="/settings/stoppage" />
</PageLayout>
);
};
export default StoppagesPage;
Refactor the mapped JSX into an actual React component so it can use the useEffect hook (and all other React hooks).
Example:
const Item = ({ item }) => {
const [newItems, setNewItems] = useState([]);
useEffect(() => {
fetch(`/api/newItems?newItemId=${item.id}`)
.then((response) => response.json())
.then((data) => setNewItems(data))
.catch((error) => {
throw error;
});
}, []);
return (
<TableRow>
<TableCell>{item.name}</TableCell>
<TableCell>{item.latitude}</TableCell>
<TableCell>{item.longitude}</TableCell>
<TableCell>
{newItems.map((newItem) => newItem.name).join(", ")}
</TableCell>
<TableRow/>
);
};
...
const StoppagesPage = () => {
...
return (
<PageLayout
menu={<SettingsMenu />}
breadcrumbs={['settingsTitle', 'settingsStoppages']}
>
<Table>
<TableHead>
<TableRow>
<TableCell>{t('settingsStoppage')}</TableCell>
<TableCell>{t('settingsCoordinates')}</TableCell>
<TableCell>{t('sharedRoutes')}</TableCell>
<TableCell className={classes.columnAction} />
</TableRow>
</TableHead>
<TableBody>
{loading
? <TableShimmer columns={2} endAction />
: items.map((item) => <Item key={item.id} item={item} />)
}
</TableBody>
</Table>
<CollectionFab editPath="/settings/stoppage" />
</PageLayout>
);
};
But I need to place data inside a table and render them as well. My question was simple. Since I can't fetch data inside the JSX, On the other hand I need item.id to fetch. So how would I fetch data by item.id and render it inside the table cell?
Example:
{!loading ? items.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.name}</TableCell>
<TableCell>{`Latitude: ${item.latitude}, Longitude: ${item.longitude}`}</TableCell>
<TableCell>
{
# need to fetch and render data here
geofences.map((geofence) => geofence.name).join(', ')
}
</TableCell>
<TableCell className={classes.columnAction} padding="none">
<CollectionActions itemId={item.id} editPath="/settings/stoppage" endpoint="stoppages" setTimestamp={setTimestamp} />
</TableCell>
</TableRow>
)) : (<TableShimmer columns={2} endAction />)}

Why my re-render page after filter doesn't working?

in my project I show a list where all the pokemons categories are.
When the user clicks on a certain category the list is updated.
My list is updating, but the problem is that my component is not re-rendering again with the new list items.
Here's my code I put into codesandbox
import React from "react";
import { types, pokemons } from "./data";
import Avatar from "./components/Avatar";
import List from "./components/List";
import "./styles.css";
const App = () => {
const [favorite, setFavorite] = React.useState("rock");
console.log(favorite);
const _data = [];
React.useMemo(
() =>
pokemons.map((pokemon, i) => {
if (pokemon.type.includes(favorite.toLowerCase())) {
_data.push(pokemon);
}
return _data;
}),
[_data, favorite]
);
const removeDup = [];
_data.reduce((acc, curr) => {
if (acc.indexOf(curr.name) === -1) {
acc.push(curr.name);
removeDup.push(curr);
}
return acc;
}, []);
return (
<div className="App">
<Avatar data={types} setFavorite={setFavorite} />
<List data={removeDup} />
</div>
);
};
export default App;
List
const List = ({ data }) => {
const [pokemonsState, setPokemonsState] = useState(data);
const [isAscSort, setIsAscSort] = useState(false);
const sortPokemon = () => {
if (isAscSort)
setPokemonsState(stableSort(data, getComparator("asc", "name")));
else setPokemonsState(stableSort(data, getComparator("desc", "name")));
setIsAscSort(!isAscSort);
};
return (
<TableContainer>
<Table sx={{ minWidth: 650 }}>
<TableHead>
<TableRow>
<TableCell>Pokémon</TableCell>
<TableCell name onClick={() => sortPokemon()} align="right">
Name
{!isAscSort ? <ArrowUpward /> : <ArrowDownward />}
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{pokemonsState.map((pokemon, idx) => (
<TableRow
key={idx}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row" thumbnailImage>
<div className="thumb">
<img src={pokemon.thumbnailImage} alt="" />
</div>
</TableCell>
<TableCell align="left" component="th" scope="row" description>
{pokemon.name}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};
Could you tell me what I'm doing wrong?
Thank you very much in advance!!!
Issues
You've at least a couple issues.
Using the mapped array index as the React key is generally an anti-pattern, especially if you are filtering, sorting, mutating the underlying array being mapped.
The List component doesn't update its pokemonsState state when the data prop updates.
Solution
Use a useEffect with a dependency on the data prop to update the local pokemonsState state. use the pokemon.id as the React key, assuming all pokemon have unique id properties.
const List = ({ data }) => {
const [pokemonsState, setPokemonsState] = useState(data);
const [isAscSort, setIsAscSort] = useState(false);
// Update local state when prop updates
useEffect(() => {
setPokemonsState(data);
}, [data]);
const sortPokemon = () => {
if (isAscSort)
setPokemonsState(stableSort(data, getComparator("asc", "name")));
else setPokemonsState(stableSort(data, getComparator("desc", "name")));
setIsAscSort(!isAscSort);
};
return (
<TableContainer>
<Table sx={{ minWidth: 650 }}>
...
<TableBody>
{pokemonsState.map((pokemon, idx) => (
<TableRow
key={pokemon.id} // <-- use unique React key
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
...
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};
Your List component is trying to copy the data prop into its state. Copying props into state is usually a bad idea. If that prop changes, the List will ignore the change and continue using its state. Only once something changes the state (eg, clicking on the sort button) will you get back in sync.
I would recommend that you delete the state and instead compute the sorted list from the prop. This computation can be put inside of useMemo to improve performance by skipping calculating if nothing has changed:
const List = ({ data }) => {
const [isAscSort, setIsAscSort] = useState(false);
const sortedPokemons = useMemo(() => {
if (isAscSort) {
return stableSort(data, getComparator("asc", "name"))
} else {
return stableSort(data, getComparator("desc", "name"))
}
}, [data, isAscSort]);
const sortPokemon = () => {
setIsAscSort(!isAscSort);
};
// ...
<TableBody>
{sortedPokemons.map((pokemon, idx) => (
// ...
)}
</TableBody>
}

How to render API data in ReactJs by unique ID?

I have a page where every data saved in the database is rendering in a table with very limited information, I have an action button (Detail) to view all the information for the particular company.
Code for the table:
const PendingApplication = () => {
let history = useHistory();
const [data, setData] = useState([]);
const handleClick = (location) => {
console.log(location);
history.push(location);
};
useEffect(() => {
axios
.get('http://localhost:5000/api/kam')
.then((response) => {
console.log(response);
setData(response.data.kamData);
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div className="content">
<Table>
<TableHead>
<TableRow>
<TableCell>Ticket No</TableCell>
<TableCell align="right">Category</TableCell>
<TableCell align="right">Sub Category</TableCell>
<TableCell align="right">Request Time & Date</TableCell>
<TableCell align="right">Company Name</TableCell>
<TableCell align="right">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((item, index) => (
<TableRow key={index}>
<TableCell>{item.ticketno}</TableCell>
<TableCell>{item.approvecategory}</TableCell>
<TableCell>{item.subcategory}</TableCell>
<TableCell>{item.date}</TableCell>
<TableCell>{item.companyname}</TableCell>
<TableCell>
<Button color="#71BD44" onClick={() => handleClick('/detail')}>
Detail
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
};
export default PendingApplication;
Here is the code for detail page:
const Details = () => {
const setPopup = useContext(SetPopupContext);
let history = useHistory();
const [data, setData] = useState([]);
const handleClick = (location) => {
console.log(location);
history.push(location);
};
useEffect(() => {
axios
.get('http://localhost:5000/api/kam/:id')
.then((response) => {
console.log(response);
setData(response.data.kamData);
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div className="content">
<Box
sx={{
width: '90%',
padding: '24px 20px', // theme padding
border: '1px solid rgba(0, 0, 0, 0.12)',
borderRadius: 4,
}}
>
<div className="ticket-details">
<h3>TICKET DETAILS</h3>
{data.map((item, index) => (
<TableRow>
<p>Ticket ID: {item.ticketno}</p>
<p>Category: {item.approvecategory}</p>
<p>Category: {item.subcategory}</p>
<p>Category: {item.date}</p>
</TableRow>
))}
</div>
<div className="additional-info">
<h3>ADDITONAL INFO</h3>
{data.map((item, index) => (
<TableRow>
<p>Company Name: {item.companyname}</p>
<p>KCP Name: {item.kcpname}</p>
<p>KCP Contact No: {item.kcpcontact}</p>
<p>KCP NID No: {item.kcpnid}</p>
<p>No of MSISDN: {item.msisdn}</p>
</TableRow>
))}
</div>
</Box>
</div>
);
};
export default Details;
I have created the API for unique ID, Here is the API:
router.get('/kam/:id', (req, res) => {
console.log(req.params.id);
kamForm
.findById(req.params.id)
.then((result) => {
res.status(200).json({
kamData: result,
});
})
.catch((err) => {
console.log(err);
res.status(500).json({
message: err,
});
});
});
After clicking the detail button i want that particular info in detail page, can anyone help me, how to do that?
In your Database, you must have an id column, send that column also with your data into fetch API. In your code, you will get item.id, use that id for the handleClick button.
see below code.
const PendingApplication = () => {
let history = useHistory();
const [data, setData] = useState([]);
const handleClick = (id) => {
console.log(id);
//use id here
history.push(location);
};
useEffect(() => {
axios
.get('http://localhost:5000/api/kam')
.then((response) => {
console.log(response);
setData(response.data.kamData);
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div className="content">
<Table>
<TableHead>
<TableRow>
<TableCell>Ticket No</TableCell>
<TableCell align="right">Category</TableCell>
<TableCell align="right">Sub Category</TableCell>
<TableCell align="right">Request Time & Date</TableCell>
<TableCell align="right">Company Name</TableCell>
<TableCell align="right">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((item, index) => (
<TableRow key={index}>
<TableCell>{item.ticketno}</TableCell>
<TableCell>{item.approvecategory}</TableCell>
<TableCell>{item.subcategory}</TableCell>
<TableCell>{item.date}</TableCell>
<TableCell>{item.companyname}</TableCell>
<TableCell>
<Button color="#71BD44" onClick={() => handleClick(item.id)}>
Detail
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
};
export default PendingApplication;

Render data as a functional component with React.js and Material-UI

I have a problem with the management of the data, when i try to render some data from the pokemon api my table render multiple times the titles, i tried to move only the data to a different component but not luck.
How can i fix this?
API CAll
export const PokemonApi = () => {
const [poke, setPoke] = useState([]);
const data = () => {
axios.get('https://pokeapi.co/api/v2/pokemon?limit=10&offset=20').then(( response ) => {
setPoke(response.data.results);
console.log(response.data.results);
})
.catch( err => {
console.log(err);
})
}
useEffect(() => {
data()
}, []);
return (
<>
{
poke.map(( info, name ) => {
return <Lista key={ name } info={ info } />
})
}
</>
)
}
component where I try to render
export const Lista = (props) => {
const classes = useStyles();
return (
<div>
<Container maxWidth="md">
<TableContainer component={Paper}>
<Table className={ classes.table } size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell align="right">URL</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow key={ props.info.name }>
<TableCell component="th" scope="row">
{ props.info.name }
</TableCell>
<TableCell align="right">{ props.info.url }</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Container>
</div>
)
}
This is the page where i render the table
import React from 'react'
import { PokemonApi } from '../api/PokemonApi'
export const Pokes = () => {
return (
<>
<PokemonApi />
</>
)
}
And here is the table.
I hope anyone can help me!
As your code is written, you are not rendering one table with a row for each line. You are creating one Lista par record, you have as many tables as pokemon.
What you are looking to achieve is more like :
function PokemonRow(props) {
return (
<TableRow key={ props.info.name }>
<TableCell component="th" scope="row">
{ props.info.name }
</TableCell>
<TableCell align="right">{ props.info.url }</TableCell>
</TableRow>
)
}
export const PokemonTable() {
const classes = useStyles();
const [poke, setPoke] = useState([]);
const data = () => {
axios.get('https://pokeapi.co/api/v2/pokemon?limit=10&offset=20').then(( response ) => {
setPoke(response.data.results);
console.log(response.data.results);
})
.catch( err => {
console.log(err);
})
}
useEffect(() => {
data()
}, []);
return (
<div>
<Container maxWidth="md">
<TableContainer component={Paper}>
<Table className={ classes.table } size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell align="right">URL</TableCell>
</TableRow>
</TableHead>
<TableBody>
{poke.map(infos => <PokemonRow info={infos}/>)}
</TableBody>
</Table>
</TableContainer>
</Container>
</div>
)
}

How can I get data from json with axios? (React)

I'm trying to getting this JSON data
{
ID string `json:"id"`
Amount int `json:"amount"`
Month string `json:"month"`
PayFailed bool `json:"pay_failed"`
}
and I wrote my code like this.
but I don't think this code can get data. I did console.log() but nothing come up. so
I don't know how to check to get data successfully.
const Pay = props => {
const { user, month,} = props;
const classes = useStyles();
const [Pay, setPay] = useState([]);
useEffect(() => {
axios
.get(https://test)
.then(res => {
setPay(res.data);
})
.catch(err => {
alert("error");
});
}, [user]);
return (
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell >date of pay</TableCell>
<TableCell >amont</TableCell>
<TableCell >pay</TableCell>
</TableRow>
</TableHead>
<TableBody>
{
Pay.filter(pay => pay.month === month).map(pay => (
pay.data.map((pay, index) => (
<TableRow key={index}>
<TableCell>{pay.DeletedAt}</TableCell>
<TableCell>{pay.amount}</TableCell>
<TableCell>{pay.pay_failed}</TableCell>
</TableRow>
)
)))
}
</TableBody>
</Table>
);
};
export default PayDone;
Does anyone know how to get it?
Can you please share some more detail? Like if you are having any errors or what the data looks like in your end, as we don't have access to the exact API. Add a console.log before setPay(res.data); and see what it returns.
Although not important in your case, Why are you doing the nested map? In your JSON schema, there is no object field. Instead all are atomic values.
Pay.filter(pay => pay.month === month).map((pay, index) => (
<TableRow key={index}>
<TableCell>{pay.DeletedAt}</TableCell>
<TableCell>{pay.amount}</TableCell>
<TableCell>{pay.pay_failed}</TableCell>
</TableRow>
))

Categories

Resources