how could i pass the key prop to a function? - javascript

i have a key prop error that i can't resolve. I guess i should pass the key prop but it does not work. Here is my map function
<TableRow key={row.id} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell component="th" scope="row">
<Person lastname={row.lastname} firstname={row.firstname} />
</TableCell>
{CalculateHours().map((c) => ( DisplayDate(c, {row}) ))}
</TableRow>
And here is my function:
const DisplayDate = (c, {row}) => {
if (ParsingTime(row.startDate) > c && ParsingTime(row.startDate) < c+1) {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr1} width={calculatedWidth(ParsingTime(row.startDate), true)}/></TableCell>
} else if(ParsingTime(row.endDate) > c && ParsingTime(row.endDate) < c+1) {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr2} width={calculatedWidth(ParsingTime(row.endDate), false)} /></TableCell>
} else if(ParsingTime(row.startDate) > c || ParsingTime(row.endDate) < c+1) {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr3} /></TableCell>
} else {
return <TableCell key={c} style={cellStyle}><hr style={hrStyle.hr1} /></TableCell>
}
};
How can i pass the key ? Thanks for answers

The react docs says that the keys should be in sibling objects -> https://reactjs.org/docs/lists-and-keys.html#:~:text=Example%3A%20Incorrect%20Key%20Usage
So for your code, it should be in the DisplayDate component when doing the map.

Related

CheckBox not highlighting

When I am selecting any Row the checkboxes aren't getting highlighted. Sometimes even if I selected any row then every rows gets selected automatically. I am using Material UI Table in ReactJs
const headCells = [
{
id: "sl_no",
numeric: true,
disablePadding: false,
label: "Sl_no",
},
{
id: "business_code",
numeric: false,
disablePadding: false,
label: "Business_Code",
},
//removed most columns soo as to post the question easily but can refer to image for the column names
function EnhancedTableHead(props) {
const {
onSelectAllClick,
order,
orderBy,
numSelected,
rowCount,
onRequestSort,
} = props;
const createSortHandler = (property) => (event) => {
onRequestSort(event, property);
};
return (
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
color="primary"
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={onSelectAllClick}
inputProps={{
"aria-label": "select all desserts",
}}
/>
</TableCell>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric ? "right" : "left"}
padding={headCell.disablePadding ? "none" : "normal"}
sortDirection={orderBy === headCell.id ? order : false}
>
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : "asc"}
onClick={createSortHandler(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{order === "desc" ? "sorted descending" : "sorted ascending"}
</Box>
) : null}
</TableSortLabel>
</TableCell>
))}
</TableRow>
</TableHead>
);
}
export default function EnhancedTable() {
const [selected, setSelected] = React.useState([]);
const handleSelectAllClick = (event) => {
if (event.target.checked) {
const newSelecteds = rows.map((n) => n.name);
setSelected(newSelecteds);
return;
}
setSelected([]);
};
const handleClick = (event, name) => {
const selectedIndex = selected.indexOf(name);
let newSelected = [];
if (selectedIndex === -1) {
newSelected = newSelected.concat(selected, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selected.slice(1));
} else if (selectedIndex === selected.length - 1) {
newSelected = newSelected.concat(selected.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selected.slice(0, selectedIndex),
selected.slice(selectedIndex + 1)
);
}
setSelected(newSelected);
};
const isSelected = (name) => selected.indexOf(name) !== -1;
// Avoid a layout jump when reaching the last page with empty rows.
const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
return (
<Box sx={{ width: "100%" }}>
<Paper sx={{ width: "100%", mb: 2 }}>
<EnhancedTableToolbar numSelected={selected.length} />
<TableContainer>
<Table
sx={{ minWidth: 750 }}
aria-labelledby="tableTitle"
size={dense ? "small" : "medium"}
>
<EnhancedTableHead
numSelected={selected.length}
order={order}
orderBy={orderBy}
onSelectAllClick={handleSelectAllClick}
onRequestSort={handleRequestSort}
rowCount={rows.length}
/>
<TableBody>
rows.slice().sort(getComparator(order, orderBy)) */}
{stableSort(rows, getComparator(order, orderBy))
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => {
const isItemSelected = isSelected(row.name);
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
onClick={(event) => handleClick(event, row.sl_no)}
role="checkbox"
aria-checked={isItemSelected}
tabIndex={-1}
key={row.sl_no}
selected={isItemSelected}
>
<TableCell padding="checkbox">
<Checkbox
color="primary"
checked={isItemSelected}
inputProps={{
"aria-labelledby": labelId,
}}
/>
</TableCell>
<TableCell
component="th"
id={labelId}
scope="row"
padding="none"
>
{row.sl_no}
</TableCell>
<TableCell align="center">{row.business_code}</TableCell>
</TableRow>
);
})}
{emptyRows > 0 && (
<TableRow
style={{
height: (dense ? 33 : 53) * emptyRows,
}}
>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
</Table>
</TableContainer>
</Paper>
</Box>
);
}
This is how the the invoice table looks

How to sort table by name?

I'm still a beginner in ReactJS and I'm doing a job where I need to sort my list of pokemons by name.
I'm using the Material-UI Table, but I can't do the ordering. Here's is my code I put into codesandbox
I know there are duplicate values in my list, but this is a problem that I will solve after I fix the order ^^
import React from "react";
import {
Table,
TableBody,
TableContainer,
TableHead,
TableRow,
TableCell
} from "#material-ui/core";
import { pokemons } from "./data";
import "./styles.css";
const App = () => {
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === "desc"
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) {
return order;
}
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
}
return (
<TableContainer>
<Table sx={{ maxWidth: 650 }}>
<TableHead>
<TableRow>
<TableCell>Pokémon</TableCell>
<TableCell align="right">Name</TableCell>
</TableRow>
</TableHead>
<TableBody>
{pokemons.map((pokemon) => (
<TableRow
key={pokemon.name}
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>
);
};
export default App;
Could you tell me what I'm doing wrong?
Thank you very much in advance.
Hey i just made some improvements in your code. You can check it out now.
import React, { useState } from "react";
import {
Table,
TableBody,
TableContainer,
TableHead,
TableRow,
TableCell,
} from "#material-ui/core";
import { pokemons } from "./data";
import "./styles.css";
const App = () => {
const [pokemonsState, setPokemonsState] = useState(pokemons);
const [isAsecSort, setIsAsecSort] = useState(false);
const onClickName = () => {
if (isAsecSort)
setPokemonsState(stableSort(pokemons, getComparator("aesc", "name")));
else setPokemonsState(stableSort(pokemons, getComparator("desc", "name")));
setIsAsecSort(!isAsecSort);
};
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === "desc"
? (a, b) => descendingComparator(a, b, orderBy)
: (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) {
return order;
}
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
}
return (
<TableContainer>
<Table sx={{ maxWidth: 650 }}>
<TableHead>
<TableRow>
<TableCell>Pokémon</TableCell>
<TableCell align="right" onClick={() => onClickName()}>
Name
</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">
<div className="thumb">
<img src={pokemon.thumbnailImage} alt="" />
</div>
</TableCell>
<TableCell align="left" component="th" scope="row">
{pokemon.name}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};
export default App;
Things I changed -:
Used the sorting function with 'name' as comprable.
In pockemon mapping on DOM I used index as key because name was repeating.
You used some weird fields (like thumbnailImage) in JSX element. So I removed them.
Now it is completely error free.
Edit 1 -: To sort the array only after clicking name I added onClickListener on Name element (to make some hover effect you can use css or button/anchor tag). For automatic rerendering I used useState hook for storing pokemons instead of normal array.
Edit 2 -: To toggle the sort on each click I added a isAsecSort counter which will sort in ascending order if true else will sort in descending order.

how to send the data from one component to another component when i press a button. React.js

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

How to update the value present inside array of array in reactjs

Working Code :https://codesandbox.io/s/broken-https-2i454?file=/src/App.js
I'm using Material UI in my reactjs project and I'm trying the update the value entered inside a textfield of a table using onChange function for the textfield, currently the value is present inside an array of array,I want to update the componentQuantity , I'm aware of passing the event to get the event.target.value , but what is the correct way to update the specific value of the textfield present inside a array of array. Please someone help me out here.
class TabData extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
data: [
{
BomComponentCode: "345543",
BomComponentName: "COMP1",
BomComponentRefUOM: "gm",
rowId: 0,
consumptionBatchNumbers: [
{
componentBatchNumber: "20",
componentQuantity: 2
},
{
componentBatchNumber: "21",
componentQuantity: 3
}
]
},
//2nd cloumn
{
BomComponentCode: "5543",
BomComponentName: "COMP2",
BomComponentRefUOM: "KG",
rowId: 1,
consumptionBatchNumbers: [
{
componentBatchNumber: "22",
componentQuantity: 4
},
{
componentBatchNumber: "23",
componentQuantity: 5
}
]
}
],
renderState: false
};
}
handleUpdate = (index, Code) => {
this.setState({
renderState: Code
});
};
handleChange = (e) => {
console.log(e.target.value)
};
render() {
const { classes } = this.props;
const { data, renderState } = this.state;
return (
<div className={classes.list}>
<React.Fragment>
<Grid
item
xs={12}
sm={12}
md={12}
className={classes.grid}
style={{ paddingLeft: "0" }}
>
<p variant="h6" className={classes.font}>
Table Details
</p>
<span className={classes.borders}></span>
</Grid>
<div>
<TableContainer component={Paper} className={classes.paper}>
<Table className={classes.table} aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell> Number</TableCell>
<TableCell> Text</TableCell>
<TableCell>UOM</TableCell>
<TableCell> Batch </TableCell>
<TableCell> Quantity</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
<React.Fragment>
{data.map((item, i) => (
<React.Fragment>
<TableRow key={i}>
<TableCell scope="row" align="left">
{item.BomComponentCode}
</TableCell>
<TableCell align="left">
{item.BomComponentName}
</TableCell>
<TableCell align="left">
{item.BomComponentRefUOM}
</TableCell>
<TableCell>
{item.consumptionBatchNumbers.map((row, i) => (
<div key={i}>
<TableCell align="left">
{row.componentBatchNumber}
</TableCell>
</div>
))}
</TableCell>
{renderState === item.BomComponentCode ? (
<TableCell align="left">
{item.consumptionBatchNumbers.map((row, indi) => (
<div key={indi}>
<input
// value={row.componentQuantity}
onChange={(e) =>
this.handleChange(e)
}
defaultValue={row.componentQuantity}
/>
</div>
))}
</TableCell>
) : (
<TableCell align="left">
{item.consumptionBatchNumbers.map((row, indi) => (
<div key={indi}>
<TableCell align="left">
{row.componentQuantity}
</TableCell>
</div>
))}
</TableCell>
)}
<TableCell>
<button
onClick={() =>
this.handleUpdate(i, item.BomComponentCode)
}
>
Update
</button>
</TableCell>
</TableRow>
</React.Fragment>
))}
</React.Fragment>
</TableBody>
</Table>
</TableContainer>
</div>
<Grid container justify="center">
<Grid
item
xs={4}
sm={4}
md={2}
style={{
textAlign: "center",
padding: "1rem"
}}
>
<button >Close</button>
</Grid>
</Grid>
</React.Fragment>
</div>
);
}
}
export default (TabData);
To modify the value in the deeply nested array you need indexes . Once you have the indexes its easier to change the values .
Change the handleChange method to accept the row and deeply nested row's indexes.
onChange={(e) => this.handleChange(e, i, indi)}
Once you have the indexes we can deep clone the original state to create a new copy of state and mutate it directly .
handleChange = (e, rowIndex, consumptionBatchIndex) => {
// deep clone the data
const clonedData = JSON.parse(JSON.stringify(this.state.data));
clonedData[rowIndex].consumptionBatchNumbers[
consumptionBatchIndex
].componentQuantity = e.target.value;
this.setState({
data: clonedData
});
};
Now we can read the value in the input as
<input
value={row.componentQuantity}
onChange={(e) => this.handleChange(e, i, indi)}
/>

Javascript Can't read second element of array that is array

I am trying to access all elements of my array except the first one but i get empty values.
renderTableBody = thisService => {
console.log(thisService); // in the image it's the printed array
console.log(thisService.length); // in the image is the number 1
return thisService.slice(1).map(item =>
item.map(k => (
<TableRow key={k.kpiId}>
<TableCell align="left" component="th" scope="row">
{k.kpiId}
</TableCell>
<TableCell align="left">{k.kpiName}</TableCell>
<TableCell align="left">{k.targetDisplay}</TableCell>
<TableCell align="left">{k.targetDisplay}</TableCell>
<TableCell align="left">{k.reason}</TableCell>
<TableCell align="left">{k.action}</TableCell>
<TableCell align="left">{k.dueDate}</TableCell>
<TableCell align="left">{k.owner}</TableCell>
</TableRow>
))
);
};
When i log the length of array it prints 1 when it actually has 2 elements. When i try to access only the second element of the array, i get "undefined".
This is the logged to console object:
Am i doing something wrong in accessing the second element of my array which is an array itself?
EDIT
The part of code where i am calling the function renderTableBody:
<div id="servicesInputs">
{kpiServInputs.map((service) => (
<div key={service[0].id} id={service[0].id}>
<div align="left">
<h4>{service[0].serviceName}</h4>
</div>
<div>
<Paper >
<Table>
<TableHead>
<TableRow>
<TableCell align="left">#</TableCell>
<TableCell align="left">KPI</TableCell>
<TableCell align="left">Target</TableCell>
<TableCell align="left">Actual</TableCell>
<TableCell align="left">Reason</TableCell>
<TableCell align="left">Action</TableCell>
<TableCell align="left">Due Date</TableCell>
<TableCell align="left">Owner</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.renderTableBody(service)}
</TableBody>
</Table>
</Paper>
<br/>
</div>
</div>
))}
</div>
renderTableBody = thisService => {
console.log(thisService); // in the image it's the printed array
console.log(thisService.length); // in the image is the number 1
return thisService[1].map(item =>
item.map(k => (
<TableRow key={k.kpiId}>
<TableCell align="left" component="th" scope="row">
{k.kpiId}
</TableCell>
<TableCell align="left">{k.kpiName}</TableCell>
<TableCell align="left">{k.targetDisplay}</TableCell>
<TableCell align="left">{k.targetDisplay}</TableCell>
<TableCell align="left">{k.reason}</TableCell>
<TableCell align="left">{k.action}</TableCell>
<TableCell align="left">{k.dueDate}</TableCell>
<TableCell align="left">{k.owner}</TableCell>
</TableRow>
))
);
};
This should be your code because as #Dupocas pointed out, thisService is an array that looks like [{Object}, [Array]].
When you run slice on this and a map on the result, what you are doing is trying to map through the outer array of something like [['stuff you need']]. This outer array has a length of 1 and would act exactly as you are seeing it act. By changing the slice to directly access the first index of thisService, you will be looping though the info you need.
The problem was that the array was populated asynchronously with making a fetch call to an api. What worked for me was making the return async and waiting the fetch call to finish and then populating the array.
The code:
function fetchKpiServicesInputs(state) {
let serviceInputs = [];
return async dispatch => {
for (var i = 0; i < servicesCount; i++) {
let newServ = [];
newServ.push({id: i, serviceName: kpiServices[i].name });
let url = "apiUrl";
const response = await fetch(url, {
method: 'GET'
});
const responseData = await response.json();
if (Object.keys(responseData).length > 0) {
newServ.push(responseData);
}
serviceInputs.push(newServ);
}
dispatch(receiveKpiServiceInputs(serviceInputs));
};
Thanks for the help everyone!

Categories