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>
))
Related
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 />)}
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>
}
I have a problem selecting a single checkbox or multiple checkbox in a table in React. I'm using Material-UI. Please see my codesandbox here
CLICK HERE
I wanted to achieve something like this in the picture below:
<TableContainer className={classes.tableContainer}>
<Table>
<TableHead className={classes.tableHead}>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
checked={false}
inputProps={{ "aria-label": "select all desserts" }}
/>
</TableCell>
{head.map((el) => (
<TableCell key={el} align="left">
{el}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{body?.excluded_persons?.map((row, index) => (
<TableRow key={row.id}>
<TableCell padding="checkbox">
<Checkbox checked={true} />
</TableCell>
<TableCell align="left">{row.id}</TableCell>
<TableCell align="left">{row.name}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
Seems you are just missing local component state to track the checked status of each checkbox, including the checkbox in the table header.
Here is the implementation for the AddedPersons component since it's more interesting because it has more than one row of data.
Create state to hold the selected persons state. Only add the additional local state, no need to duplicate the passed body prop data (this is anti-pattern anyway) nor add any derived state, i.e. is indeterminate or is all selected (also anti-pattern).
const [allSelected, setAllSelected] = React.useState(false);
const [selected, setSelected] = React.useState({});
Create handlers to toggle the states.
const toggleAllSelected = () => setAllSelected((t) => !t);
const toggleSelected = (id) => () => {
setSelected((selected) => ({
...selected,
[id]: !selected[id]
}));
};
Use a useEffect hook to toggle all the selected users when the allSelected state is updated.
React.useEffect(() => {
body.persons?.added_persons &&
setSelected(
body.persons.added_persons.reduce(
(selected, { id }) => ({
...selected,
[id]: allSelected
}),
{}
)
);
}, [allSelected, body]);
Compute the selected person count to determine if all users are selected manually or if it is "indeterminate".
const selectedCount = Object.values(selected).filter(Boolean).length;
const isAllSelected = selectedCount === body?.persons?.added_persons?.length;
const isIndeterminate =
selectedCount && selectedCount !== body?.persons?.added_persons?.length;
Attach all the state and callback handlers.
return (
<>
<TableContainer className={classes.tableContainer}>
<Table>
<TableHead className={classes.tableHead}>
<TableRow>
<TableCell colSpan={4}>{selectedCount} selected</TableCell>
</TableRow>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
checked={allSelected || isAllSelected} // <-- all selected
onChange={toggleAllSelected} // <-- toggle state
indeterminate={isIndeterminate} // <-- some selected
inputProps={{ "aria-label": "select all desserts" }}
/>
</TableCell>
...
</TableRow>
</TableHead>
<TableBody>
{body?.persons?.added_persons?.map((row, index) => (
<TableRow key={row.id}>
<TableCell padding="checkbox">
<Checkbox
checked={selected[row.id] || allSelected} // <-- is selected
onChange={toggleSelected(row.id)} // <-- toggle state
/>
</TableCell>
<TableCell align="left">{row.id}</TableCell>
<TableCell align="left">{row.name}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
);
Update
Seems there was a bug in my first implementation that disallowed manually deselecting people while the select all checkbox was checked. The fix is to move the logic in the useEffect into the toggleAllSelected handler and use the onChange event to toggle all the correct states. Also to add a check to toggleSelected to deselect "select all" when any person checkboxes have been deselected.
const [allSelected, setAllSelected] = React.useState(false);
const [selected, setSelected] = React.useState({});
const toggleAllSelected = (e) => {
const { checked } = e.target;
setAllSelected(checked);
body?.persons?.added_persons &&
setSelected(
body.persons.added_persons.reduce(
(selected, { id }) => ({
...selected,
[id]: checked
}),
{}
)
);
};
const toggleSelected = (id) => (e) => {
if (!e.target.checked) {
setAllSelected(false);
}
setSelected((selected) => ({
...selected,
[id]: !selected[id]
}));
};
Note: Since both AddedPersons and ExcludedPersons components are basically the same component, i.e. it's a table with same headers and row rendering and selected state, you should refactor these into a single table component and just pass in the row data that is different. This would make your code more DRY.
I have updated your added person table as below,
please note that I am using the component state to update the table state,
const AddedPersons = ({ classes, head, body }) => {
const [addedPersons, setAddedPersons] = useState(
body?.persons?.added_persons.map((person) => ({
...person,
checked: false
}))
);
const [isAllSelected, setAllSelected] = useState(false);
const [isIndeterminate, setIndeterminate] = useState(false);
const onSelectAll = (event) => {
setAllSelected(event.target.checked);
setIndeterminate(false);
setAddedPersons(
addedPersons.map((person) => ({
...person,
checked: event.target.checked
}))
);
};
const onSelect = (event) => {
const index = addedPersons.findIndex(
(person) => person.id === event.target.name
);
// shallow clone
const updatedArray = [...addedPersons];
updatedArray[index].checked = event.target.checked;
setAddedPersons(updatedArray);
// change all select checkbox
if (updatedArray.every((person) => person.checked)) {
setAllSelected(true);
setIndeterminate(false);
} else if (updatedArray.every((person) => !person.checked)) {
setAllSelected(false);
setIndeterminate(false);
} else {
setIndeterminate(true);
}
};
const numSelected = addedPersons.reduce((acc, curr) => {
if (curr.checked) return acc + 1;
return acc;
}, 0);
return (
<>
<Toolbar>
{numSelected > 0 ? (
<Typography color="inherit" variant="subtitle1" component="div">
{numSelected} selected
</Typography>
) : (
<Typography variant="h6" id="tableTitle" component="div">
Added Persons
</Typography>
)}
</Toolbar>
<TableContainer className={classes.tableContainer}>
<Table>
<TableHead className={classes.tableHead}>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
checked={isAllSelected}
inputProps={{ "aria-label": "select all desserts" }}
onChange={onSelectAll}
indeterminate={isIndeterminate}
/>
</TableCell>
{head.map((el) => (
<TableCell key={el} align="left">
{el}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{addedPersons?.map((row, index) => (
<TableRow key={row.id}>
<TableCell padding="checkbox">
<Checkbox
checked={row.checked}
onChange={onSelect}
name={row.id}
/>
</TableCell>
<TableCell align="left">{row.id}</TableCell>
<TableCell align="left">{row.name}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
);
};
export default AddedPersons;
Please refer to this for a working example: https://codesandbox.io/s/redux-react-forked-cuy51
I'm using a axios to get my api to display some data from it. This works fine.
I want to get each of value and display the returned data when I click "TableRow"
this is my json data.
I want to get id and use axios api like this.
const toDetails = (e) => {
e.preventDefault();
const getDetails = async () => {
const response = await axios.get(`api/firstmemory/${id}`);
setUserData(response.data.data);
}
getDetails();
}
inside return
return(
<TableContainer component={Paper}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>things</TableCell>
<TableCell >date</TableCell>
</TableRow>
</TableHead>
<TableBody>
{userData.map((row,index) => (
<TableRow key={index}>
<TableCell>{row.first}</TableCell>
<TableCell>{row.date}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
Can anyone help me to figure this out ? Thank you.
You can add onClickto handle the click on the row and data-id attribute to store the id value on the row.
<TableRow key={index} onClick={handleRowClick} data-id={row.id}>
<TableCell>{row.first}</TableCell>
<TableCell>{row.date}</TableCell>
</TableRow>
Then you can read the data-id attribute's value in the click handler with
function handleRowClick(e) {
let id = e.currentTarget.getAttribute('data-id')
// Your axios code here
}
I have a table with players that is loaded in from an API call and rendered using a map function. The last column of the table contains a delete button. When clicking the delete button, I would like to disable all the other delete buttons until the delete call is completed.
Here is the function that performs the API call to get the players.
loadPlayers() {
const { cookies } = this.props;
const AuthStr = 'Bearer '.concat(this.state.access_token);
axios.get(
configVars.apiUrl + 'team/get-team',
{ headers: { Authorization: AuthStr } }
).then(response => {
var teamArray = response.data;
//Information gotten
this.setState({
teamArray: teamArray,
});
}).catch((error) => {
//Error, remove the access token from cookies and redirect home.
cookies.remove('access_token');
this.props.history.push("/");
});
}
}
Mapping and rendering is done like this:
<Grid item xs={12}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell align="right">Tier</TableCell>
<TableCell align="right">Value</TableCell>
<TableCell align="right">Delete</TableCell>
</TableRow>
</TableHead>
{this.state.teamArray.map((player, i) => {
return <TableBody key={i}>
<TableRow key={i}>
<TableCell align="left">{player.full_name}</TableCell>
<TableCell align="right">{player.tier}</TableCell>
<TableCell align="right">{player.value}</TableCell>
<TableCell align="right">
<IconButton disabled={this.state.deleteDisable}
onClick={() => {this.handleDelete(player.id)}} >
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
</TableBody>;
})}
</Table>
</Grid>
Inside the handleDelete function I start by setting deleteDisabledin the state to true. However, this has no effect since disabled is set to false once the table is loaded and never changed after.
How do I make sure this.state.deleteDisable is passed to the button as a variable instead of assigned once?
You should store the players into the state, then in the render method you can display the table
function loadPlayer() {
const { cookies } = this.props;
const AuthStr = 'Bearer '.concat(this.state.access_token);
axios.get(
configVars.apiUrl + 'team/get-team',
{ headers: { Authorization: AuthStr } }
)
.then(response => this.setState({players: response.data})})
.catch((error) => {
// Error ....
});
}
render() {
return (
...
{
this.state.players((player, i) => (
<TableBody key={i}>
<TableRow>
<TableCell align="left">{player.full_name}</TableCell>
<TableCell align="right">{player.tier}</TableCell>
<TableCell align="right">{player.value}</TableCell>
<TableCell align="right">
<IconButton disabled={this.state.deleteDisabled}
onClick={() => {this.handleDelete(player.id)}} >
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
</TableBody>
)}
...
);
}