I've a react Table with multiple rows and 3 columns, every row has an edit button, when user clicks on the edit button of any row, i want the name data from that row to appear in the input textfield above.
I've tried Ref and setting up the state but none of them are working, please help.
Also, I've have a react-select field as well so please tell me if the same solution should apply to that?
Here is the full code https://codesandbox.io/s/naughty-nobel-xboze
import React, { useState, useRef } from "react";
import TextField from "#material-ui/core/TextField";
import {
Table,
TableCell,
TableBody,
TableHead,
TableRow
} from "#material-ui/core";
const data = [
{
id: 1,
name: "tom1"
},
{
id: 2,
name: "tom2"
},
{
id: 3,
name: "tom3"
},
{
id: 4,
name: "mike"
}
];
const Demo = () => {
const key = useRef(null);
const [word, setWord] = useState("");
const focusText = (name) => {
key.current.value = name;
};
return (
<React.Fragment>
<TextField
style={{ width: "100%" }}
id="outlined-basic"
variant="outlined"
value={word}
size="small"
color="primary"
ref={key}
onChange={e => {
setWord(e.target.value);
}}
/>
<Table>
<TableHead>
<TableRow>
<TableCell>id</TableCell>
<TableCell>name</TableCell>
<TableCell>action</TableCell>
</TableRow>
</TableHead>
<TableBody>{data.map(item => (
<TableRow>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.id}</p>
</TableCell>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.name}</p>
</TableCell>
<TableCell>
<button onClick={() => focusText(item.name)}>edit</button>
</TableCell>
</TableRow>
))}</TableBody>
</Table>
</React.Fragment>
);
};
export default Demo;
Sandbox link https://codesandbox.io/s/eloquent-bardeen-972th
import React, { useState, useRef } from "react";
import TextField from "#material-ui/core/TextField";
import {
Table,
TableCell,
TableBody,
TableHead,
TableRow
} from "#material-ui/core";
const data = [
{
id: 1,
name: "tom1"
},
{
id: 2,
name: "tom2"
},
{
id: 3,
name: "tom3"
},
{
id: 4,
name: "mike"
}
];
const Demo = () => {
const key = useRef(null);
const [word, setWord] = useState("");
const focusText = (id, name) => {
setWord(name);
};
return (
<>
<TextField
style={{ width: "100%" }}
id="outlined-basic"
variant="outlined"
value={word}
size="small"
color="primary"
ref={key}
onChange={e => {
setWord(e.target.value);
}}
/>
<Table>
<TableHead>
<TableRow>
<TableCell>id</TableCell>
<TableCell>name</TableCell>
<TableCell>action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{
data.map(item => (
<TableRow key={item.id}>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.id}</p>
</TableCell>
<TableCell align="left">
<p style={{ fontSize: "13px", margin: "0px" }}>{item.name}</p>
</TableCell>
<TableCell>
<button onClick={() => focusText(item.id, item.name)}>edit</button>{" "}
</TableCell>
</TableRow>
))
}</TableBody>
</Table>
</>
);
};
export default Demo;
Related
I need to take time of change checkBox. You can see that when 1000 people render in the one page, checkbox works so slow and I make the test project which I will push to issue in github. So I need the time of change checkBox to do the correctly benchmark of this, but idk how to take this time.
That I was able to do is a console.log(event.target) of checkBox and in console You can see the slow work and lug if click this checkBox many times.
The code and ref to the project https://github.com/fordelord/render-issue-reproduction :
import { useEffect, useState } from "react";
import Checkbox from "#mui/material/Checkbox";
import {
FormControlLabel,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from "#mui/material";
interface Person {
id: string;
firstName: string;
lastName: string;
username: string;
email: string;
img: string;
address: {
street: string;
city: string;
zipCode: string;
};
phone: string;
}
export default function Home() {
const [people, setPeople] = useState<Person[]>([]);
const [usersList, setUsersList] = useState<string[]>([]);
useEffect(() => {
setPeople(randUser({ length: 1000 }));
}, []);
return (
<TableContainer component={Paper} sx={{ fontFamily: "Arial" }}>
<Table aria-label="simple table">
<TableHead>
<TableRow
sx={{
"& .MuiTableCell-root": {
fontSize: "20px",
textAlign: "center",
},
}}
>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Id</TableCell>
<TableCell>Number</TableCell>
<TableCell>CheckBox</TableCell>
</TableRow>
</TableHead>
<TableBody>
{people.map((person) => (
<TableRow
key={person.id}
sx={{
"&:nth-of-type td, &:nth-of-type th": { border: 0 },
"& .MuiTableCell-root": {
textAlign: "center",
},
}}
>
<TableCell component="th" scope="row">
{person.firstName}
</TableCell>
<TableCell component="th" scope="row">
{person.email}
</TableCell>
<TableCell>{person.id}</TableCell>
<TableCell>{person.phone}</TableCell>
<TableCell>
<FormControlLabel
id={person.id}
control={
<Checkbox
sx={{
"&.Mui-checked": {
color: "rgb(75, 180, 128)",
},
}}
checked={usersList.includes(person.id)}
onClick={(event) => {
console.log(event.target);
}}
/>
}
label="Checkbox"
onChange={() => {
if (usersList.includes(person.id)) {
const updatedPlayerList = usersList.filter(
(playerId) => playerId !== person.id
);
setUsersList(updatedPlayerList);
} else {
setUsersList([...usersList, person.id]);
}
}}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
Can someone help me?
I'm creating an app in react where if i click on a particular date or select the range of the date so the data should show of that date in a below table. if there is no date is selected that there will be zero record in that table. only when the date is selected than the record of that date or date range show in the table BUT I'M FAILED TO DO THAT IF ANYONE KNOWS HOW CAN I SOLVE THAT ISSUE THAN PLEASE HELP ME.... and theres one more issue here i'm using static data or limited no. of that, is there any way that i can create a dynamic data for the columns that i'm using in table so when i select any date than the data of that date should show in that table....
import { useEffect, useRef, useState } from "react";
import { DateRange } from "react-date-range";
import Table from "#mui/material/Table";
import TableBody from "#mui/material/TableBody";
import TableCell from "#mui/material/TableCell";
import TableContainer from "#mui/material/TableContainer";
import TableHead from "#mui/material/TableHead";
import TableRow from "#mui/material/TableRow";
import Paper from "#mui/material/Paper";
import format from "date-fns/format";
import { addDays } from "date-fns";
import "react-date-range/dist/styles.css";
import "react-date-range/dist/theme/default.css";
function createData(id, fromDate, toDate, name, calories, fat, carbs, protein) {
return { id, fromDate, toDate, name, calories, fat, carbs, protein };
}
const rows = [
createData(1, "22/11,2022", "ytuy", 159, 6, 2, 4),
createData(2, "23/11,2022", "Froz", 15, 6, 4, 3),
createData(3, "24/11,2022", "jhg", 19, 4, 14, 0),
createData(4, "25/11,2022", "gfj", 59, 3, 34, 1),
createData(5, "26/11,2022", "ytyu", 1, 2, 24, 4),
];
const DateRangeComp = () => {
const [range, setRange] = useState([
{
startDate: new Date(),
endDate: addDays(new Date(), 7),
key: "selection",
},
]);
const [open, setOpen] = useState(false);
const refOne = useRef(null);
useEffect(() => {
document.addEventListener("keydown", hideOnEscape, true);
document.addEventListener("click", hideOnClickOutside, true);
}, []);
const hideOnEscape = (e) => {
if (e.key === "Escape") {
setOpen(false);
}
};
const hideOnClickOutside = (e) => {
if (refOne.current && !refOne.current.contains(e.target)) {
setOpen(false);
}
};
return (
<div className="calendarWrap" style={{ marginBottom: "36px" }}>
<input
style={{ width: "15.9rem" }}
value={`${format(range[0].startDate, "MM/dd/yyyy")} to ${format(
range[0].endDate,
"MM/dd/yyyy"
)}`}
readOnly
className="inputBox"
onClick={() => setOpen((open) => !open)}
/>
<div ref={refOne}>
{open && (
<DateRange
onChange={(item) => setRange([item.selection])}
editableDateInputs={true}
moveRangeOnFirstSelection={false}
ranges={range}
months={1}
direction="horizontal"
className="calendarElement"
/>
)}
</div>
<>
<TableContainer component={Paper} style={{ marginTop: "30px" }}>
<Table
style={{ width: "100%" }}
size="small"
aria-label="a dense table"
>
<TableHead>
<TableRow>
<TableCell>S.No.</TableCell>
<TableCell align="center">From Date</TableCell>
<TableCell align="center">Dessert (100g serving)</TableCell>
<TableCell align="center">Calories</TableCell>
<TableCell align="center">Fat (g)</TableCell>
<TableCell align="center">Carbs (g)</TableCell>
<TableCell align="center">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.id}
</TableCell>
<TableCell align="center">{row.fromDate}</TableCell>
<TableCell align="center">{row.name}</TableCell>
<TableCell align="center">{row.calories}</TableCell>
<TableCell align="center">{row.fat}</TableCell>
<TableCell align="center">{row.carbs}</TableCell>
<TableCell align="center">{row.protein}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
</div>
);
};
export default DateRangeComp;
I want to know how to execute 2 functions handleClose and saveData at the same time in the onClick method.
The place I wanted to execute:
<Button variant="contained" onClick={saveData}>
Save
</Button>
Full code:
import { useEffect } from "react";
import * as React from "react";
import Table from "#mui/material/Table";
import TableBody from "#mui/material/TableBody";
import TableCell from "#mui/material/TableCell";
import TableContainer from "#mui/material/TableContainer";
import TableHead from "#mui/material/TableHead";
import TableRow from "#mui/material/TableRow";
import Paper from "#mui/material/Paper";
import Button from "#mui/material/Button";
import Box from "#mui/material/Box";
import LinearProgress from "#mui/material/LinearProgress";
import Backdrop from "#mui/material/Backdrop";
import Modal from "#mui/material/Modal";
import Fade from "#mui/material/Fade";
import TextField from "#mui/material/TextField";
const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
p: 4,
};
const Home = () => {
const [data, setData] = React.useState([]);
const [loader, setLoader] = React.useState(false);
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const [formData, setFormData] = React.useState({
id: null,
avatar: "",
name: "",
});
const saveData = () => {
fetch("https://61924463aeab5c0017105ebe.mockapi.io/test", {
method: "POST",
body: JSON.stringify(formData),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
})
.then((response) => response.json())
.then((json) => {
console.log(json, "Save Data");
handleClose();
});
};
useEffect(() => {
setLoader(true);
fetch("https://61924463aeab5c0017105ebe.mockapi.io/test")
.then((res) => res.json())
.then(
(result) => {
if (result) {
setData(result);
setLoader(false);
}
},
(error) => {
console.log(error);
}
);
}, []);
console.log(formData);
return (
<div>
<Button variant="contained" onClick={handleOpen}>
Add
</Button>
<br />
<br />
{loader ? (
<LinearProgress />
) : (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell align="center">ID</TableCell>
<TableCell align="center">Name</TableCell>
<TableCell align="center">Avatar</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map((row) => (
<TableRow
key={row.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell align="center">{row.id}</TableCell>
<TableCell align="center">{row.name}</TableCell>
<TableCell align="center">
<img src={row.avatar} width="25" alt={row.name} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
)}
<div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<Box sx={style}>
<Box
component="form"
sx={{
"& .MuiTextField-root": { m: 1, width: "25ch" },
}}
noValidate
autoComplete="off"
>
<div>
<TextField
id="outlined-search"
label="ID"
type="search"
onChange={(text) => {
setFormData({
...formData,
id: text.target.value,
});
}}
/>
<TextField
id="outlined-search"
label="Name"
type="search"
onChange={(text) => {
setFormData({
...formData,
name: text.target.value,
});
}}
/>
<TextField
id="outlined-search"
label="Avatar"
type="search"
onChange={(text) => {
setFormData({
...formData,
avatar: text.target.value,
});
}}
/>
</div>
</Box>
<Button variant="contained" onClick={saveData}>
Save
</Button>
</Box>
</Fade>
</Modal>
</div>
</div>
);
};
export default Home;
<Button variant="contained" onClick={() => {
saveData();
handleClose();
}}>
Save
</Button>
The most straight forward method is to wrap them in a jsx prop.
If you did not get what I mean see the example below:
Example:
import react from "react";
const App = () =>{
return(
<div id="foo-bar">
<button onClick={()=>{
foo()
foo2();
}}/>
</div>
);
}
You can be called any handler or function on the onClick method.
Live Example:
const App = () => {
const saveData = () => {
console.log("save data called");
};
const handleClose = () => {
console.log("handleClose");
};
const handleOnClick = () => {
handleClose();
saveData();
};
return (
<div>
<button onClick={handleOnClick}>Save</button>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am using material UI Collapsible Table: https://material-ui.com/components/tables/#table
I want to show dynamic data in the head and rows and want to repeat the head and rows so that if i click on any row(dynamic) it collapses.
I want to show data from this api in the table: http://wms-api.martoo.com/api/wms-orders
Currently the static data is showing but I am unable to show dyamic data from redux store.
import React, {useEffect} from 'react';
import { connect } from "react-redux";
import PropTypes from 'prop-types';
import {homeActions} from '../../store/home/actions'
import { makeStyles } from '#material-ui/core/styles';
import Box from '#material-ui/core/Box';
import Collapse from '#material-ui/core/Collapse';
import IconButton from '#material-ui/core/IconButton';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Typography from '#material-ui/core/Typography';
import Paper from '#material-ui/core/Paper';
import KeyboardArrowDownIcon from '#material-ui/icons/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#material-ui/icons/KeyboardArrowUp';
const useRowStyles = makeStyles({
root: {
'& > *': {
borderBottom: 'unset',
},
},
});
function createData(name, calories, fat, carbs, protein, price) {
return {
name,
calories,
fat,
carbs,
protein,
price,
history: [
{ date: '2020-01-05', customerId: '11091700', amount: 3 },
{ date: '2020-01-02', customerId: 'Anonymous', amount: 1 },
],
};
}
function Row(props) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
return (
<React.Fragment>
<TableRow className={classes.root}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.calories}</TableCell>
<TableCell align="right">{row.fat}</TableCell>
<TableCell align="right">{row.carbs}</TableCell>
<TableCell align="right">{row.protein}</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
History
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.history.map((historyRow) => (
<TableRow key={historyRow.date}>
<TableCell component="th" scope="row">
{historyRow.date}
</TableCell>
<TableCell>{historyRow.customerId}</TableCell>
<TableCell align="right">{historyRow.amount}</TableCell>
<TableCell align="right">
{Math.round(historyRow.amount * row.price * 100) / 100}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
Row.propTypes = {
row: PropTypes.shape({
calories: PropTypes.number.isRequired,
carbs: PropTypes.number.isRequired,
fat: PropTypes.number.isRequired,
history: PropTypes.arrayOf(
PropTypes.shape({
amount: PropTypes.number.isRequired,
customerId: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
}),
).isRequired,
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
protein: PropTypes.number.isRequired,
}).isRequired,
};
const CollapsibleTable = (props) => {
useEffect(() => {
props.allOrders();
}, []);
let AllOrdersresult = props.allOrdersState.map(item => ({
order_id: item.order.order_id,
net_total: item.order.net_total,
tax_total: item.order.tax_total,
date_created: item.order.date_created.split(' ')[0],
shipping_total: item.order.shipping_total,
product_qty: item.product_qty,
first_name: item.customer.first_name,
last_name: item.customer.last_name,
email: item.customer.email,
}));
const rows = [
{
name:'shafiq',
calories:'test',
fat:'test fat',
carbs:'test carbs',
protein:'test protein',
price:2000,
history: [
{ 'date': '2020-01-05', 'customerId': '11091700', 'amount': 3 },
{ 'date': '2020-01-02', 'customerId': 'Anonymous', 'amount': 1 },
],
},
{
name:'Mazhar',
calories:'test',
fat:'test fat',
carbs:'test carbs',
protein:'test protein',
price:2000,
history: [
{ 'date': '2020-01-05', 'customerId': '11091700', 'amount': 3 },
{ 'date': '2020-01-02', 'customerId': 'Anonymous', 'amount': 1 },
],
},
{
name:'Hanan Khan',
calories:'test',
fat:'test fat',
carbs:'test carbs',
protein:'test protein',
price:2000,
history: [
{ 'date': '2020-01-05', 'customerId': '11091700', 'amount': 3 },
{ 'date': '2020-01-02', 'customerId': 'Anonymous', 'amount': 1 },
],
}
];
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell />
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<Row key={row.name} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
const mapStateToProps = state => {
return {
allOrdersState:state.homeReducer.allOrders,
};
};
const mapDispatchToProps = {
allOrders:homeActions.allOrders,
// allStocks:homeActions.allStocks,
// allUsers:homeActions.allUsers
};
const connectCollapsibleTable = connect(
mapStateToProps,
mapDispatchToProps
)(CollapsibleTable);
export { connectCollapsibleTable as CollapsibleTable };
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
enter image description here
I have solved the problem. since i am populating data from redux store so I take an empty array, then i take for loop and pushed objects inside array and showed the appropriate object items in the concerned tableCells.
Kindly have a look at my CollapsibleTable Component and compare this with MaterialUI collapsible tables then you would have better idea how to populate dynamic data in the table rows.
import React, {useEffect} from 'react';
import { connect } from "react-redux";
import PropTypes from 'prop-types';
import {homeActions} from '../../store/home/actions'
import { makeStyles } from '#material-ui/core/styles';
import Box from '#material-ui/core/Box';
import Collapse from '#material-ui/core/Collapse';
import IconButton from '#material-ui/core/IconButton';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableContainer from '#material-ui/core/TableContainer';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Typography from '#material-ui/core/Typography';
import Paper from '#material-ui/core/Paper';
import KeyboardArrowDownIcon from '#material-ui/icons/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#material-ui/icons/KeyboardArrowUp';
import Button from '#material-ui/core/Button';
const useRowStyles = makeStyles({
root: {
'& > *': {
borderBottom: 'unset',
},
},
});
function Row(props) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
return (
<React.Fragment>
<TableRow className={classes.root}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<TableCell component="th" scope="row">{row.order_id}</TableCell>
<TableCell align="right">{row.date_created}</TableCell>
<TableCell align="right">{row.status}</TableCell>
<TableCell align="right">{row.total_sales}</TableCell>
<TableCell align="right">
<Button variant="contained" color="primary">Order Again </Button>
</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
History
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.history.map((historyRow) => (
<TableRow key={historyRow.date}>
<TableCell component="th" scope="row">
{historyRow.date}
</TableCell>
<TableCell>{historyRow.customerId}</TableCell>
<TableCell align="right">{historyRow.amount}</TableCell>
<TableCell align="right">
{Math.round(historyRow.amount * row.price * 100) / 100}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
Row.propTypes = {
row: PropTypes.shape({
calories: PropTypes.number.isRequired,
carbs: PropTypes.number.isRequired,
fat: PropTypes.number.isRequired,
history: PropTypes.arrayOf(
PropTypes.shape({
amount: PropTypes.number.isRequired,
customerId: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
}),
).isRequired,
name: PropTypes.string.isRequired,
price: PropTypes.number.isRequired,
protein: PropTypes.number.isRequired,
}).isRequired,
};
const CollapsibleTable = (props) => {
useEffect(() => {
props.allOrders();
},[]);
const ordersTest = [];
for (let i = 0; i < props.allOrdersState.length && props.allOrdersState.length; i++) {
ordersTest.push({
order_id: props.allOrdersState.length ? props.allOrdersState[0]['order']['order_id'] :'',
total_sales: props.allOrdersState.length ? props.allOrdersState[0]['order']['total_sales'] :'',
status: props.allOrdersState.length ? props.allOrdersState[0]['order']['status'] :'',
tax_total:props.allOrdersState.length ? props.allOrdersState[0]['order']['tax_total'] :'',
date_created: props.allOrdersState.length ? props.allOrdersState[0]['order']['date_created'] :'',
shipping_total: props.allOrdersState.length ? props.allOrdersState[0]['order']['shipping_total'] :'',
product_qty: props.allOrdersState.length ? props.allOrdersState[0]['product_qty'] :'',
first_name: props.allOrdersState.length ? props.allOrdersState[0]['customer']['first_name'] :'',
last_name: props.allOrdersState.length ? props.allOrdersState[0]['customer']['last_name'] :'',
email: props.allOrdersState.length ? props.allOrdersState[0]['customer']['email'] :'',
history: [
{ date: '2020-01-05', customerId: '11091700', amount: 3 },
{ date: '2020-01-02', customerId: 'Anonymous', amount: 1 },
],
});
}
console.log('all orders for each....', ordersTest);
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead style={{background:'#f4511e'}}>
<TableRow>
<TableCell />
<TableCell>Order No.</TableCell>
<TableCell align="right">Date</TableCell>
<TableCell align="right">Status</TableCell>
<TableCell align="right">Total</TableCell>
<TableCell align="right">Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ordersTest.slice(0, 10).map((row) => (
<Row key={row.order_id} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
const mapStateToProps = state => {
return {
allOrdersState:state.homeReducer.allOrders,
};
};
const mapDispatchToProps = {
allOrders:homeActions.allOrders,
// allStocks:homeActions.allStocks,
// allUsers:homeActions.allUsers
};
const connectCollapsibleTable = connect(
mapStateToProps,
mapDispatchToProps
)(CollapsibleTable);
export { connectCollapsibleTable as CollapsibleTable };
I am trying to develop a simple React app, with crud operations, but I can't even list my objects, which are stored into a sql database. I followed this example for the frontend: https://github.com/only2dhir/react-js-example
and I continued it, adding the component for my objects.
In my application, I have doctors and patients. A patient is assigned to a doctor, so a doctor can have one or more patients. In order to do this, I create my jsx file, ListCaregiverComponent.jsx, where I did that :
import React, { Component } from 'react'
import ApiServiceCaregiver from "../../service/ApiServiceCaregiver";
import ApiServicePatient from "../../service/ApiServicePatient";
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Typography from '#material-ui/core/Typography';
class ListCaregiverComponent extends Component {
constructor(props) {
super(props)
this.state = {
caregivers: [],
patients: [],
message: null
}
this.deleteCaregiver = this.deleteCaregiver.bind(this);
this.editCaregiver = this.editCaregiver.bind(this);
this.addCaregiver = this.addCaregiver.bind(this);
this.reloadCaregiverList = this.reloadCaregiverList.bind(this);
this.reloadPatientList = this.reloadPatientList.bind(this);
}
componentDidMount() {
this.reloadCaregiverList();
this.reloadPatientList();
}
reloadCaregiverList() {
ApiServiceCaregiver.fetchCaregivers()
.then((res) => {
this.setState({caregivers: res.data.result})
});
}
reloadPatientList() {
ApiServicePatient.fetchPatients()
.then((res) => {
this.setState({patients: res.data.result})
});
}
deleteCaregiver(userId) {
ApiServiceCaregiver.deleteCaregiver(userId)
.then(res => {
this.setState({message : 'User deleted successfully.'});
this.setState({caregivers: this.state.caregivers.filter(user => user.id !== userId)});
})
}
editCaregiver(id) {
window.localStorage.setItem("userId", id);
this.props.history.push('/edit-caregiver');
}
addCaregiver() {
window.localStorage.removeItem("userId");
this.props.history.push('/add-caregiver');
}
render() {
return (
<div>
<br></br>
<br></br>
<Typography variant="h4" style={style}>Caregiver Details</Typography>
<br></br>
<br></br>
<Table>
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell align="right">Name</TableCell>
<TableCell align="right">Birth Date</TableCell>
<TableCell align="right">Gender</TableCell>
<TableCell align="right">Address</TableCell>
<TableCell align="center">Patients</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.caregivers.map(({ id, name, birthDate, gender, address, patients = [] }) =>(
<TableRow key={id}>
<TableCell component="th" scope="row">
{id}
</TableCell>
<TableCell align="right">{name}</TableCell>
<TableCell align="right">{birthDate}</TableCell>
<TableCell align="right">{gender}</TableCell>
<TableCell align="right">{address}</TableCell>
<TableCell align="right">
<TableRow>
<TableCell align = "right" >Id</TableCell>
<TableCell align = "right" >Name</TableCell>
<TableCell align="right">Birth Date</TableCell>
<TableCell align="right">Gender</TableCell>
<TableCell align="right">Address</TableCell>
<TableCell align="right">Medical Record</TableCell>
</TableRow>
{patients.map(({ id, name, birthDate, gender, address, medicalRecord })=> {
return (
<TableRow key={id}>
<TableCell component="th" scope="row">
{id}
</TableCell>
<TableCell align="right">{name}</TableCell>
<TableCell align="right">{birthDate}</TableCell>
<TableCell align="right">{gender}</TableCell>
<TableCell align="right">{address}</TableCell>
<TableCell align="right">{medicalRecord}</TableCell>
</TableRow>
)
})}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
}
const style ={
display: 'flex',
justifyContent: 'center'
}
export default ListCaregiverComponent;
The ListPatientComponent.jsx looks like this :
import React, { Component } from 'react'
import ApiServicePatient from "../../service/ApiServicePatient";
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import Typography from '#material-ui/core/Typography';
class ListPatientComponent extends Component {
constructor(props) {
super(props)
this.state = {
patients: [],
message: null
}
this.deletePatient = this.deletePatient.bind(this);
this.editPatient = this.editPatient.bind(this);
this.addPatient = this.addPatient.bind(this);
this.reloadPatientList = this.reloadPatientList.bind(this);
}
componentDidMount() {
this.reloadPatientList();
}
reloadPatientList() {
ApiServicePatient.fetchPatients()
.then((res) => {
this.setState({patients: res.data.result})
});
}
deletePatient(userId) {
ApiServicePatient.deletePatient(userId)
.then(res => {
this.setState({message : 'User deleted successfully.'});
this.setState({patients: this.state.patients.filter(user => user.id !== userId)});
})
}
editPatient(id) {
window.localStorage.setItem("userId", id);
this.props.history.push('/edit-patient');
}
addPatient() {
window.localStorage.removeItem("userId");
this.props.history.push('/add-patient');
}
render() {
return (
<div>
<br></br>
<br></br>
<Typography variant="h4" style={style}>Patient Details</Typography>
<br></br>
<br></br>
<Table>
<TableHead>
<TableRow>
<TableCell>Id</TableCell>
<TableCell align="right">Name</TableCell>
<TableCell align="right">Birth Date</TableCell>
<TableCell align="right">Gender</TableCell>
<TableCell align="right">Address</TableCell>
<TableCell align="right">Medical Record</TableCell>
<TableCell align="center">Medication Plans</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.state.patients.map(row => (
<TableRow key={row.id}>
<TableCell component="th" scope="row">
{row.id}
</TableCell>
<TableCell align="right">{row.name}</TableCell>
<TableCell align="right">{row.birthDate}</TableCell>
<TableCell align="right">{row.gender}</TableCell>
<TableCell align="right">{row.address}</TableCell>
<TableCell align="right">{row.medicalRecord}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
}
const style ={
display: 'flex',
justifyContent: 'center'
}
export default ListPatientComponent;
and it works.
I also added two new js files, ApiServicePatient.js and ApiServiceCaregiver.js :
import axios from 'axios';
const CAREGIVER_API_BASE_URL = 'http://localhost:8080/caregivers';
class ApiServiceCaregiver {
fetchCaregivers() {
return axios.get(CAREGIVER_API_BASE_URL);
}
fetchCaregiverById(caregiverId) {
return axios.get(CAREGIVER_API_BASE_URL + '/' + caregiverId);
}
deleteCaregiver(caregiverId) {
return axios.delete(CAREGIVER_API_BASE_URL + '/' + caregiverId);
}
addCaregiver(caregiver) {
return axios.post(""+CAREGIVER_API_BASE_URL, caregiver);
}
editCaregiver(caregiver) {
return axios.put(CAREGIVER_API_BASE_URL + '/' + caregiver.id, caregiver);
}
}
export default new ApiServiceCaregiver();
import axios from 'axios';
const PATIENT_API_BASE_URL = 'http://localhost:8080/patients';
class ApiServicePatient {
fetchPatients() {
return axios.get(PATIENT_API_BASE_URL);
}
fetchPatientById(userId) {
return axios.get(PATIENT_API_BASE_URL + '/' + userId);
}
deletePatient(userId) {
return axios.delete(PATIENT_API_BASE_URL + '/' + userId);
}
addPatient(user) {
return axios.post(""+PATIENT_API_BASE_URL, user);
}
editPatient(user) {
return axios.put(PATIENT_API_BASE_URL + '/' + user.id, user);
}
}
export default new ApiServicePatient();
Also, in App.js, I added :
import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import ListUserComponent from "./component/user/ListUserComponent";
import AddUserComponent from "./component/user/AddUserComponent";
import EditUserComponent from "./component/user/EditUserComponent";
import ListPatientComponent from "./component/patient/ListPatientComponent";
import ListCaregiverComponent from "./component/caregiver/ListCaregiverComponent";
function App() {
return (
<div className="container">
<Router>
<div className="col-md-6">
<h1 className="text-center" style={style}>React User Application</h1>
<Switch>
<Route path="/" exact component={ListUserComponent} />
<Route path="/users" component={ListUserComponent} />
<Route path="/add-user" component={AddUserComponent} />
<Route path="/edit-user" component={EditUserComponent} />
<Route path="/patients" component={ListPatientComponent} />
<Route path="/caregivers" component={ListCaregiverComponent} />
</Switch>
</div>
</Router>
</div>
);
}
const style = {
color: 'red',
margin: '10px'
}
export default App;
It doesn't work, it only says :
Unhandled Rejection (TypeError): Cannot read property 'map' of undefined
and it indicates the lines with :
<TableCell align="right">{row.address}</TableCell>
{row.patients.map(row => (
In the backend application, the class Caregiver has a list of patients objects, mapped as one-to-many :
#OneToMany(mappedBy = "caregiver", fetch = FetchType.EAGER)
private List<Patient> patients;
Does anyone have some suggestions?
--------UPDATE AFTER FIRST ANSWERS---------
I no longer get that error, but I can't display in my table of caregivers the assigned patients. The corresponding TableCell s are empty for the patients of each caregiver. Why is happening this?
You do this.state.caregivers.map(row => and then {row.patients.map(row =>, but I don't see any indication that each caregivers element has a patients array property
Since you are using asynchronous call the javascript start rendering and for a few miliseconds "this.state.caregivers" has no value so you cannot map a null value it needs to be array null.
Make sure to add a condition to check first or declare "this.state.caregivers" as empty array in the constructor.
As mentioned in other answers, patients is maybe undefined for some rows, so you could assign it a default value using Destructuring:
{this.state.caregivers.map(({ id, name, birthDate, gender, address, patients = [] }) => (
<TableCell component="th" scope="row">
{id}
</TableCell>
<TableCell align="right">{name}</TableCell>
<TableCell align="right">{birthDate}</TableCell>
<TableCell align="right">{gender}</TableCell>
<TableCell align="right">{address}</TableCell>
{patients.map(row => (
...
Try to add before each map the property you want to map and && like this:
this.state.caregivers && this.state.caregivers.map
Do this for each map in your code.