So I am currently trying to display data in a table. This data is from 2 separate tables in the database with foreign keys.
I get my list using this call:
useEffect(()=>{
axios.get("http://localhost:3001/stores").then((response)=>{
setlistofStores(response.data) //State which contains the response from the API request
});
}, []);
So I can get the list of Stores and can display them in the table with no issue using this code:
<TableBody>
{listofStores.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => (
<TableRow key={row.tenantName}>
<TableCell>
<Grid container>
<Grid item lg={2}>
<Avatar alt={row.unit} src='.' className={classes.avatar}/>
</Grid>
<Grid item lg={10}>
<Typography className={classes.name}>{row.unit}</Typography>
</Grid>
</Grid>
</TableCell>
<TableCell>{row.contactName}</TableCell>
<TableCell>
<Typography
className={classes.status}
style={{
flex: 'center',
backgroundColor:
((row.industry === 'Apparel' && 'purple') ||
(row.industry === 'F&B' && 'grey') ||
(row.industry === 'Admin' && 'red') ||
(row.industry === 'Tech' && 'blue'))
}}
>{row.industry}</Typography>
</TableCell>
<TableCell>{row.primaryEmail}</TableCell>
<TableCell>{row.primaryPhone}</TableCell>
<TableCell className={classes.stores}>1</TableCell>
<TableCell ><button className={classes.viewButton} onClick={()=>{navigate(`/store/${row.id}`)}}>View</button></TableCell>
</TableRow>
Now I want to run this API inside each row to use the Tenant to display its data:
useEffect(() => {
axios.get(`http://localhost:3001/store/byId/${id}`).then((response) => {
setTenant(response.data);
});
}, []);
What is the correct way to do this?
useEffect with empty dependencies is the good one for your situation. You can create a new component for details and by clicking, navigate the user to that component (page). And there you can call the API for details. (or it can be a pop-up. It really depends on your UI design)
const TenantDetails = ({ tenantId, ...props }) => {
const [tenantData, setTenantData] = useState(null);
useEffect(() => {
axios.get(`http://localhost:3001/store/byId/${tenantId}`).then((response) => {
setTenantData(response.data);
});
}, []);
return (
// your UI implementation
<>
tenantData ? <div> ... </div> : <div>loading</div>
</>
)
}
Related
I'm trying to fetch data from api and show it with React.
However I could see that errors and I'm difficult with parsing json response from api.
I think that it will be solved if I make response as array.
I don't know how to make it.
page.js
I try to fetch data from API by Native hook and useEffect
I checked API by Postman. So it is working well.
function Customers(props) {
const [data, setData] = useState({});
const [error, setError] = useState(null);
useEffect(() => {
fetch("http://localhost:8080/contacts")
.then((response) => {
if (response.status !== 200) {
setError("Invalid response: ", response.status);
} else {
setError(null);
}
return response.json();
})
.then((json) => {
setData(json);
});
});
if (error !== null) {
return <div>Error: {error.message}</div>
} else {
return (
<DashboardLayout>
<>
<Head>
<title>
Data
</title>
</Head>
<Box
component="main"
sx={{
flexGrow: 1,
py: 8
}}
>
<Container maxWidth={false}>
<Box sx={{ mt: 3 }}>
<CustomerListResults customers={data} />
</Box>
</Container>
</Box>
</>
</DashboardLayout>
);
}
}
export default Customers;
list_component.js
I made a table. I would like to show API data this table.
I try to use slice and map to parsing data. but it is not working.
export const CustomerListResults = ({ customers, ...rest }) => {
const [limit, setLimit] = useState(25);
const [page, setPage] = useState(0);
const handlePageChange = (event, newPage) => {
setPage(newPage);
};
return (
<Card {...rest}>
<PerfectScrollbar>
<Box sx={{ minWidth: 1050 }}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell>
Date
</TableCell>
<TableCell>
Name
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{customers.slice(0,limit).map((customer) => (
<TableRow
hover
key={customers.id}
>
<TableCell>
<Box
sx={{
alignItems: 'center',
display: 'flex'
}}
>
<Typography
color="textPrimary"
variant="body2"
>
{customers.created_date}
</Typography>
</Box>
</TableCell>
<TableCell>
{customers.user_idx}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</PerfectScrollbar>
<TablePagination
component="div"
count={customers.length}
onPageChange={handlePageChange}
page={page}
rowsPerPage={limit}
/>
</Card>
);
};
CustomerListResults.propTypes = {
customers: PropTypes.array.isRequired
};
That's because data on initial load is equal to an empty object, and object doesn't have a slice method as it's a method for an array.
One solution is set an inital value for data to an empty array.
const [data, setData] = useState([]);
We're working on an inventory management page of sorts. The page makes a call to an external API and if that endpoint returns data, it displays that data on the page for the user. The API call uses the current logged in users ID, so we use userLoaded to wait for that ID before making the API call.
If an inventory doesn't exist (API returns nothing), we show some "Not Available" text.
However, when a page initially loads, it's showing the "Not Available" text, re-rendering a couple times, and then finally showing the correct data. Is there anyway to cut back on those re-renders or make it more graceful?
You can see the issue here: https://recordit.co/EYDLupg3xs or on the demo link here: https://showzone.io/inventory
Oddly enough, the table component is not re-rendering with the data - so it stays blank. But if you force the table to re-render (by using one of the Filters for example), the data does indeed show. How can we re-render the table component when the data is ready?
Here is the code (cut down a bit to make it easier to understand):
function Inventory() {
const { currentUser, userLoaded } = useAuth()
const [data, setData] = useState([])
const [inventoryExists, setInventoryExists] = useState(false)
const fetchData = useCallback(async () => {
if (userLoaded) {
const userInventoryData = await axios.get(
`https://showzone-api.herokuapp.com/api/user-inventory/${currentUser.uid}` // For the demo link, I hardcoded a value for testing purposes
)
setData(userInventoryData.data)
setInventoryExists(true)
}
}, [currentUser?.uid, userLoaded])
useEffect(fetchData, [fetchData])
return (
<>
.....
<TabPanel value="1" sx={{ padding: 0 }}>
<Card mb={6}>
<CardContent>
<Grid container spacing={6}>
<Grid item xs={12}>
<Typography>
<strong>Total Cards: </strong>{" "}
{inventoryExists
? data?.owned_count?.toLocaleString() +
" / " +
data?.total_card_count?.toLocaleString()
: "Not Available"}
</Typography>
<Typography>
<strong>Estimated Value: </strong>{" "}
{inventoryExists
? data?.owned_value?.toLocaleString()
: "Not Available"}
</Typography>
</Grid>
<Grid item xs={12} md={6} sx={{ display: "flex" }}>
{inventoryExists ? (
<div>
<Doughnut
data={{
datasets: [
{
data: [
Math.round(data?.owned_count),
Math.round(data?.total_card_count),
],
},
],
}}
/>
</div>
) : (
<BlankDoughnut text="Cards" />
)}
</Grid>
</Grid>
</CardContent>
</Card>
</TabPanel>
<Divider />
{inventoryExists ? (
<PlayerSearch id={currentUser?.uid} />
) : (
<PlayerSearch />
)}
</>
)
}
Using SSR in my React/Next app.
Trying to find an element with the id but is returning null
even when document is present (and I can see the div with the id plTable),
and even when getElementById is called after 6 seconds to ensure the element has been loaded on screen.
What is the issue and how can I fix this?
Here is the component:
const LineItemTable: React.FC<LineItemTableProps> = ({ reportName }) => {
const classes = useStyles({});
const dispatch = useDispatch();
const [page, setPage] = useState<number>(0);
const selectedCompanyId = useSelector((state) => state.company.selectedId);
const company = useSelector((state) => state.company.current);
useEffect(() => {
if (reportName && selectedCompanyId) {
dispatch(
getReportByName({
name: reportName, // 'profit and loss' or 'balance sheet'
includeLineItems: true,
page: page,
}),
);
}
}, [reportName, selectedCompanyId]);
let plTable: any = 'kk';
useEffect(() => {
console.log('uef');
if (typeof document !== 'undefined') {
setTimeout(() => {
plTable = document.querySelector('plTable');// ***** NEVER FOUND ******
console.log('doc', document); // ***** is found and defined correctly *****
console.log('plTable', plTable); // ***** null *****
}, 6000);
}
});
const endObserver = new IntersectionObserver(
(entries) => {
const [entry] = entries;
if (!entry.isIntersecting) {
//Put what you want to happen if the end is NOT visible
console.log('not visible');
} else {
//Put what you want to happen if the end is visible
//For instance firing your function
// setPage(page + 1);
console.log('visible');
}
},
{ root: null, threshold: 1 },
);
// endObserver.observe(plTable);
const getLineItems = useMemo(() => makeGetAllLineItemsByReport(reportName), [
reportName,
]);
const lineItems = useSelector((state) => getLineItems(state));
if (!lineItems) return null;
// ADDED
// Add an elemnt in your html with the class of "end" at the end of the chart
// I recommend adding an empty div with the class of "end" and setting it's opacity to 0
return (
<div
id="plTable" // ****** Defined here *******
style={{
display: 'flex',
alignItems: 'flex-end',
margin: 'auto 0px',
}}
>
<Grid container spacing={3}>
<Grid item xs={12}>
<Card
sx={{
padding: '20px',
}}
>
<CardContent
sx={{
alignItems: 'center',
display: 'flex',
height: '1000px',
}}
>
<Scrollbar className={classes.scrollBar}>
<Table className={classes.root}>
<TableHead>
<TableRow>
<th>
<TableCell className={classes.headerStyle}>
ANALYSIS CATEGORY
</TableCell>
<TableCell
className={classes.headerStyle}
sx={{ marginRight: '10px' }}
>
NAME
</TableCell>
{company &&
company.dates.map((header) => (
<TableCell
className={classes.headerStyle}
sx={{
width: '200px !important',
marginLeft: '10px',
}}
key={header}
>
{header}
</TableCell>
))}
</th>
</TableRow>
</TableHead>
<TableBody>
{lineItems.map((lineItem, i) => (
<TableRow key={lineItem.id}>
<LineItemRow
i={i}
id={lineItem.id}
reportName={reportName}
level={lineItem.level}
/>
</TableRow>
))}
</TableBody>
</Table>
</Scrollbar>
</CardContent>
</Card>
</Grid>
</Grid>
</div>
);
};
According to MDN docs, querySelector either takes an element to look for:
querySelector('plTable')
/* Looking for html tag plTable */
or an identifier:
querySelector('#plTable')
/* Looking for an element with id of plTable */
Don't use DOM selectors in React, use refs to access DOM nodes.
You can create a ref with useRef or React.createRef or you can pass a callback to the ref attribute of an element, that will receive the DOM node reference when the virtual DOM has done with the reconciliation.
To check if the node is mounted and do something with it being sure it is mounted, try this:
<div
id="plTable" // ****** Defined here *******
style={{
display: 'flex',
alignItems: 'flex-end',
margin: 'auto 0px',
}}
ref={node => {
if (node) console.log("p1Table", node)
//Do something with node
}}
>
I am getting an array of 5k objects from jsonplaceholder (photos endpoint). And I want to sort it by albumId using a Material-UI select. So the problem is state is not being displayed properly. When I choose 'desc' options my items grid is not being updated, but when I drop the sorting state to it's initial value, the rerender happens and I am getting a sorted by 'desc' order list.
API link: json placeholder photos JSON
const handleChangeSort = (e: SelectChangeEvent) => {
const sort = e.target.value;
setSort(sort);
};
const handleClearSort = () => {
setSort('');
};
React.useEffect(() => {
if (sort) {
const sortedImages = filteredImages.sort(
byValue((i) => i.albumId, byNumber({ desc: sort === 'desc' }))
);
setFilteredImages(sortedImages);
} else {
setFilteredImages(images);
}
setPage(1);
/* eslint-disable-next-line */
}, [sort]);
React.useEffect(() => {
console.log('NEW FILTERED IMAGES', filteredImages);
}, [filteredImages]);
<>
{!imagesLoading &&
(filteredImages.length ? (
<Grid container item spacing={5} xs={12}>
{filteredImages
.slice(itemsOnPage * (page - 1), page * itemsOnPage)
.map((image: Image) => (
<Grid item xs={4} key={image.id}>
<Card>
<CardHeader title={image.title.slice(0, 20)} />
<CardMedia
component="img"
height="100%"
image={image.thumbnailUrl}
alt={image.title}
/>
<CardActions>
<Button
size="small"
className={classNames(classes.btn, classes.deleteBtn)}
>
<DeleteIcon />
</Button>
<Button
size="small"
className={classNames(classes.btn, classes.previewBtn)}
>
<PreviewIcon />
</Button>
</CardActions>
</Card>
</Grid>
))}
</Grid>
) : (
<Grid item>
<Typography variant="body1">No images were found</Typography>
</Grid>
))}
</>
Furthermore, the useEffect, that is listening for filteredImages to change is not working. I am not getting the console.log after filteredImages change it's value.
UPD: Default sorting by Array.sort is not working either
you can use orderBy lodash for sorting array, you can also check this answer : answer
In your case, you can write useEffect method Like this
import {orderBy} from 'lodash'
React.useEffect(() => {
if (sort) {
setFilteredImages( orderBy(filteredImages, ['albumId'], ['desc']) );
} else {
setFilteredImages(images);
}
setPage(1);
/* eslint-disable-next-line */
}, [sort]);
I have a table where i render a data from the pokemon api, well in the table i have a button and when you press the button you get the data from the arrow, so i want to send that data from my component table to another component card and in that card render only the data which you select from the table. But i don't know how to send the data to my component Card whitout render my card five times in my table. Now i get the data when i press the button, i just need to send him.
Rows on the table component
export const Lista = (props) => {
const [, setPokeSelec] = useState({
})
const selectArrow = ( poke ) => {
setPokeSelec( poke );
console.log(poke);
}
return (
<>
<TableRow key={ props.info.id }>
<TableCell component="th" scope="row">
{ props.info.id }
</TableCell>
<TableCell align="right">{ props.info.name }</TableCell>
<TableCell align="right">{ props.info.abilities[0].ability.name}</TableCell>
<TableCell align="right">
<img src={ props.info.sprites.front_default } alt={ props.info.name } style={{ height: 60 }} />
</TableCell>
<TableCell align="right">
<Button
variant="contained"
color="primary"
size="small"
onClick={ () => selectArrow( props.info ) }
>
Seleccionar
</Button>
</TableCell>
</TableRow>
</>
)
}
Card component
export const Informacion = () => {
const classes = useStyles();
return (
<div className={ classes.margen } >
<Box display="flex" justifyContent="center" alignItems="center">
<Card className={classes.root}>
<CardMedia
className={classes.cover}
image={pika}
title="Live from space album cover"
/>
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography component="h5" variant="h5">
Pikachu
</Typography>
<Divider/>
<Typography variant="subtitle1" color="textPrimary">
Tipo:
</Typography>
</CardContent>
</div>
</Card>
</Box>
</div>
)
}
PokemonApi Component
Here i call the API, render the table and send to my component table the data
export const PokemonApi = () => {
const classes = useStyles();
const [poke, setPoke] = useState([]);
const data = () => {
axios.get(`https://pokeapi.co/api/v2/pokemon?limit=100`).then(( response ) => {
for(let i = 0; i < response.data.results.length; i++ ) {
axios.get(response.data.results[i].url)
.then( result => {
setPoke(prevArray => [...prevArray, result.data])
// console.log(result.data);
})
}
})
.catch( err => {
console.log(err);
})
}
useEffect(() => {
data()
}, []);
return (
<>
<TableContainer className={ classes.margin } component={Paper}>
<Table className={ classes.table } size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell align="right">Name </TableCell>
<TableCell align="right">Type </TableCell>
<TableCell align="right">Img </TableCell>
<TableCell align="right">Actions </TableCell>
</TableRow>
</TableHead>
<TableBody>
{ poke.map((infos, name) => <Lista key={name} info={infos}/>) }
</TableBody>
</Table>
</TableContainer>
</>
)
}
This is the page where i render the card and the PokemonApi
export const Pokes = () => {
return (
<Container maxWidth="md">
<PokemonApi />
<Informacion />
</Container>
)
}
Thanks for your time!!
Here are the common ways to pass data from a component A to a component B :
Having the component B as a child of the component A (see this link)
Having the component A as a child of the component B, with a callback prop that gets fired in the component A (see this link)
Using the context API
Hope that this answer helped you