How to split a component and keep props working - javascript

I have this section of a component I want to move apart and keep its props working. The way it is below works when it is within the parent component.
<TableExpandedRow key={rowExpanded.id}>
<TableCell colSpan={headers.length + 1}>
<div>
{rowExpanded &&
rowExpanded.billingItems &&
rowExpanded.billingItems.map(
item =>
rowExpanded.id ===
item.cancellationRequestId && (
<div key={item.id}>
<p>
cancellationRequestId:{' '}
{item.cancellationRequestId}
</p>
</div>
),
)}
</div>
</TableCell>
</TableExpandedRow>
So I want to make a component like this
import React from 'react';
import PropTypes from 'prop-types';
import { DataTable } from 'carbon-components-react';
const { TableExpandedRow, TableCell } = DataTable;
const TableExpandedRowComp = ({ rowExpanded, rowExpandedId, itemId, headersLength, keyId }) => (
<TableExpandedRow key={keyId}>
<TableCell colSpan={headersLength}>
<div>
{rowExpanded &&
rowExpanded.billingItems &&
rowExpanded.billingItems.map(
item =>
rowExpanded.id === item.cancellationRequestId && (
<div key={item.id}>
<p>cancellationRequestId: {item.cancellationRequestId}</p>
</div>
),
)}
</div>
</TableCell>
</TableExpandedRow>
);
TableExpandedRow.propTypes = {
rowExpanded: PropTypes.object,
headersLength: PropTypes.array,
keyId: PropTypes.object,
};
export default TableExpandedRowComp;
And then import it at where it was before like: <TableExpandedRow {...props} />
The whole component looks like this, it is a datable:
import React from 'react';
import PropTypes from 'prop-types';
import { translate } from 'react-i18next';
import { DataTable } from 'carbon-components-react';
import TableHeaders from '../TableHeaders';
import TablePagination from '../TablePagination';
import TableToolbarComp from '../TableToolbarComp';
const {
TableContainer,
TableRow,
TableExpandHeader,
TableExpandRow,
TableExpandedRow,
Table,
TableHead,
TableHeader,
TableBody,
TableCell,
} = DataTable;
function CancellationsTable({ t, tableRows }) {
return (
<div>
<DataTable
rows={tableRows}
headers={TableHeaders(t)}
render={({ rows, headers, getHeaderProps, getRowProps }) => (
<TableContainer>
<TableToolbarComp />
<Table zebra={false} short>
<TableHead>
<TableRow>
<TableExpandHeader />
{headers.map(header => (
<TableHeader {...getHeaderProps({ header })}>
{header.header}
</TableHeader>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.map(row => (
<React.Fragment key={row.id}>
<TableExpandRow {...getRowProps({ row })}>
{row.cells.map(cell => (
<TableCell key={cell.id}>{cell.value}</TableCell>
))}
</TableExpandRow>
{row.isExpanded &&
tableRows.map(
rowExpanded =>
row.id === rowExpanded.id && (
// THIS IS THE COMPONENT I WANT TO MOVE APART
<TableExpandedRow key={rowExpanded.id}>
<TableCell colSpan={headers.length + 1}>
<div>
{rowExpanded &&
rowExpanded.billingItems &&
rowExpanded.billingItems.map(
item =>
rowExpanded.id ===
item.cancellationRequestId && (
<div key={item.id}>
<p>
cancellationRequestId:{' '}
{item.cancellationRequestId}
</p>
</div>
),
)}
</div>
</TableCell>
</TableExpandedRow>
),
)}
</React.Fragment>
))}
</TableBody>
</Table>
</TableContainer>
)}
/>
</div>
);
}
CancellationsTable.propTypes = {
t: PropTypes.func.isRequired,
tableRows: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
};
export default translate()(CancellationsTable);
Any help?

I think that you're saying that you want to consume the props, and pass the props to the child untouched?
To do this, you need to destructure in a separate statement.
<DataTable
rows={tableRows}
headers={TableHeaders(t)}
render={props => {
const { rows, headers, getHeaderProps, getRowProps } = props;
// omitted
return <TableExpandedRow {...props} />
}}

Related

How to create a reusable component for large number of Buttons that open a Form

library used: mui 5.4.1
To create a TableCell containing an IconButton that opens a Form, the code is written as follows.
const data = [
{
id: "001",
name: "A",
price: 2000
},
{ id: "002", name: "B", price: 100 },
...
];
const SandboxTable = () => {
return (
<ThemeProvider theme={muiTheme}>
<TableContainer>
<Table>
...
{data.map((datum) => (
<TableRow key={datum.id}>
<TableCell>{datum.id}</TableCell>
<TableCell>{datum.name}</TableCell>
<TableCell>{datum.price}</TableCell>
<ApproveFormButtonCell
toolTip={"approve"}
id = {datum.id}
IconComponent={<CheckCircleOutlineIcon color={"success"} />}
/>
<RejectFormButtonCell
toolTip={"Reject"}
name = {datum.name}
IconComponent={<CancelOutlinedIcon color="error" />}
/>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</ThemeProvider>
);
};
const ApproveFormButtonCell = ({ toolTip, IconComponent }) => {
const usePopoverProps = usePopover();
return (
<TableCell>
<IconButtonWithTooltip
toolTip={toolTip}
onClick={usePopoverProps.handleOpen}
IconComponent={IconComponent}
/>
<BasePopover usePopverProps={usePopoverProps}>
<ApproveForm id={id} handleClose={usePopoverProps.handleClose} />
</BasePopover>
</TableCell>
);
};
Then I have to create a new ButtonCell component every time a new button is added.
To avoid this, when ApproveForm is used in SandboxTable, handleClose could not be received.
<ApproveFormButtonCell
toolTip={"approve"}
id = {datum.id}
IconComponent={<CheckCircleOutlineIcon color={"success"} />}
/>
<ApproveForm handleClose={??}/>
</ApproveFormButtonCell>
I need a lot of buttons open form.
Any good answers to solve this situation?
codesandbox
To keep from making a one-off of every sort and type of form button cell component you can abstract the common behavior/UI, i.e. the IconButtonWithTooltip and BasePopover components, into a generic component and pass in as a prop the content that is different. In this case it seems the form component is what is different.
Create a general purpose component that utilizes the power of a render prop, i.e. a prop that is a function called during the render. This render prop will pass the close handler as an argument.
Example:
const FormButton = ({ toolTip, IconComponent, renderForm }) => {
const popoverProps = usePopover();
return (
<>
<IconButtonWithTooltip
toolTip={toolTip}
onClick={popoverProps.handleOpen}
IconComponent={IconComponent}
/>
<BasePopover usePopverProps={popoverProps}>
{renderForm({ handleClose: popoverProps.handleClose })}
</BasePopover>
</>
);
};
Example usage:
import CheckCircleOutlineIcon from "#mui/icons-material/CheckCircleOutline";
import CancelOutlinedIcon from "#mui/icons-material/CancelOutlined";
import FormButton from "./FormButton";
import RejectForm from "./RejectForm";
import ApproveForm from "./ApproveForm";
...
<TableBody>
{data.map((datum) => (
<TableRow key={datum.id}>
<TableCell>{datum.id}</TableCell>
<TableCell>{datum.name}</TableCell>
<TableCell>{datum.price}</TableCell>
<TableCell>
<FormButton
toolTip={"approve"}
IconComponent={<CheckCircleOutlineIcon color={"success"} />}
renderForm={({ handleClose }) => (
<ApproveForm id={datum.id} handleClose={handleClose} />
)}
/>
</TableCell>
<TableCell>
<FormButton
toolTip={"Reject"}
IconComponent={<CancelOutlinedIcon color="error" />}
renderForm={({ handleClose }) => (
<RejectForm handleClose={handleClose} name={datum.name} />
)}
/>
</TableCell>
</TableRow>
))}
</TableBody>

How to disable the css changes made by Link/href

Currently there is a table with some columns which looks like this:
If one wants to see details about a company it must click on the arrow at the right-end of the row.
The problem appears when I want to make the whole row clickable, it destroys the design of the page even if there are no css classes added with that.
Here is the code when only the right-end arrow is clickable:
import React from 'react';
import { Table, Image, Icon } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
export default class GenericTable extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
const { headers, emptyFirstHeader, rows, entityName, idList } = this.props;
return (
<Table>
<Table.Header>
<Table.Row>
{emptyFirstHeader && <Table.HeaderCell />}
{headers.map(header => (
<Table.HeaderCell key={headers.indexOf(header)}>
{header}
</Table.HeaderCell>
))}
<Table.HeaderCell textAlign="right" />
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row, rowIndex) => (
<Table.Row key={idList && idList[rowIndex]}>
{emptyFirstHeader && (
<Table.Cell>
<Image src={row.cells[0]} />
</Table.Cell>
)}
{
(emptyFirstHeader ? row.cells.shift() : row,
row.cells.map((cell, cellIndex) => {
if (cell === undefined) {
return null;
}
return (
<Table.Cell
key={
idList && `${idList[rowIndex]} ${headers[cellIndex]}`
}>
{cell}
</Table.Cell>
);
}))
}
<Table.Cell>
<Link
to={
entityName && `/${entityName}/${idList[rows.indexOf(row)]}`
}>
<Icon name="chevron right" />
</Link>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
);
}
}
And when I include each <Table.Row> inside a <Link> it makes the row clickable but it breaks the design:
import React from 'react';
import { Table, Image, Icon } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
export default class GenericTable extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
const { headers, emptyFirstHeader, rows, entityName, idList } = this.props;
return (
<Table>
<Table.Header>
<Table.Row>
{emptyFirstHeader && <Table.HeaderCell />}
{headers.map(header => (
<Table.HeaderCell key={headers.indexOf(header)}>
{header}
</Table.HeaderCell>
))}
<Table.HeaderCell textAlign="right" />
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((row, rowIndex) => (
<Link // ADDED HERE
to={
entityName && `/${entityName}/${idList[rows.indexOf(row)]}`
}>
<Table.Row key={idList && idList[rowIndex]}>
{emptyFirstHeader && (
<Table.Cell>
<Image src={row.cells[0]} />
</Table.Cell>
)}
{
(emptyFirstHeader ? row.cells.shift() : row,
row.cells.map((cell, cellIndex) => {
if (cell === undefined) {
return null;
}
return (
<Table.Cell
key={
idList && `${idList[rowIndex]} ${headers[cellIndex]}`
}>
{cell}
</Table.Cell>
);
}))
}
<Table.Cell>
<Link
to={
entityName && `/${entityName}/${idList[rows.indexOf(row)]}`
}>
<Icon name="chevron right" />
</Link>
</Table.Cell>
</Table.Row>
</Link> // CLOSED HERE
))}
</Table.Body>
</Table>
);
}
}
How can it be done without affecting the design?
If you done need semantic HTML, you can edit the Table.Row to use Link as its components, like so. And then give the Row a class or style display: table-row
<Table.Row as={Link} style={{display: "table-row"}} to={"/"}>content of the Row</Table.Row>
You can replace the default element with the as prop and the library will pass any other props to the new component, so if you pass to it will pass the link to the Link component.

Can't read property 'map' of undefined in React app

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.

how to render components based on the value of props?

I have a parent component Dashboard.js. Here I have three values of state namely yesterday, lastWeek, lastMonth and I'm passing this value to my child component. Now I want to render my data depending on my child component. The problem is I'm using componentDidMount() lifecycle method which is rendering the child component only once. How do I render the data based on the props passed to the child component? The parent component is a react hook and the child component called DataFetchDetails is a class based component. Attaching their respective codes
Parent Component :- Dashboard.js
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import { Tabs, Tab, Grid } from '#material-ui/core';
import AppBar from '#material-ui/core/AppBar';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
import PropTypes from 'prop-types';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select'
import {
TotalUsers,
LoggedInUsers,
TimePicker,
UnregisteredUsers
} from './components';
import DataFetchDetails from './components/DataFetchDetails';
const useStyles = makeStyles(theme => ({
root: {
paddingTop: theme.spacing(4),
padding: theme.spacing(4)
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<Typography
component="div"
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
<Box p={3}>{children}</Box>
</Typography>
);
}
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
const Dashboard = () => {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
const [period, setPeriod] = React.useState("yesterday");
const handleChange1 = event => {
setPeriod(event.target.value);
};
return (
<div className={classes.root}>
<Select
labelId="demo-simple-select-label"
id="demo-sample-select"
value={time}
onChange={handleChange1}
>
<MenuItem value={"yesterday"}>Previous day</MenuItem>
<MenuItem value={"lastWeek"}>Last Week</MenuItem>
<MenuItem value={"lastMonth"}>Last Month</MenuItem>
</Select>
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value} onChange={handleChange} aria-label="simple tabs example">
<Tab label="CONSENT DETAILS" {...a11yProps(0)} />
<Tab label="ACCOUNT DETAILS" {...a11yProps(1)} />
<Tab label="DATA FETCH DETAILS" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
</TabPanel>
<TabPanel value={value} index={1}>
</TabPanel>
<TabPanel value={value} index={2}>
<DataFetchDetails period={period} handlePeriodChange1={handleChange1} />
</TabPanel>
</div>
</div>
);
};
export default Dashboard;
Child component DataFetchDetails.js :-
import React from 'react';
import {
Card,
CardHeader,
Button,
Divider,
CardContent,
TextField,
CardActions,
FormControl,
InputLabel,
Select,
MenuItem
} from '#material-ui/core';
import Paper from '#material-ui/core/Paper';
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 axios from 'axios';
import 'izitoast/dist/css/iziToast.min.css'; // loading css
import iziToast from 'izitoast/dist/js/iziToast.min.js'; // you have access to iziToast now
import 'izitoast/dist/css/iziToast.min.css';
const url = 'MY_ENDPOINT_URL';
export default class DataFetchDetails extends React.Component {
constructor(props) {
super(props);
this.state = {
items : [],
isLoaded : true,
renderJsx: false,
}
}
componentDidMount() {
this.setState({period: this.props.period});
const periodStatus = {
period : this.props.period
};
{console.log("Props period = ",this.props.period)}
axios.post(url, periodStatus)
.then((response) => {
this.setState({period : this.props.period})
this.setState({items : [response.data]});
.catch((error) => {
console.log("Error");
});
}
render() {
let {isLoaded, items, renderJsx } = this.state;
if(!isLoaded) {
return <div>Loading</div>
}
else {
return (
<div>
<div>
<Card className="Lock-user"
>
<form >
<CardHeader
title=""
/>
<Divider></Divider>
<CardContent id="form-input" className=""
>
</CardContent>
<Divider></Divider>
</form>
</Card>
</div>
<div>
<Card>
<Paper>
<Table>
<TableHead>
<TableRow>
<TableCell> success </TableCell>
<TableCell align="right">failure</TableCell>
<TableCell align="right">inProgress</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ items.map(item => (
<TableRow key={item.success}>
<TableCell component="th" scope="row">
{item.success}
</TableCell>
<TableCell align="right">{item.failure}</TableCell>
<TableCell align="right">{item.inProgress}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{console.log("Props period render = ",this.props.period)}
</Paper>
</Card>
</div>
</div>
);
}
}
}
the backend and the api works fine. I want to re render my child component based on the value of the period. How do I solve this problem?
You you compare the props i.e prevProps and current props(this.props) object inside
ComponentDidUpdate
lifecycle method to re-render the child component based on props.
As ComponentWillReceiveProps is deprecated now.
https://egghead.io/lessons/react-refactor-componentwillreceiveprops-to-getderivedstatefromprops-in-react-16-3
Go through the react lifecycle docs or https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/postrender_with_componentdidupdate.html.
Use componentWillRecieveProps in child component.
componentWillRecieveProps(props) {
// props => new props passed
// this.props => current props
}
I hope that helps.

Upload Image and indicating response in a table with axios and JavaScript

I want to upload 10 images together and send a axios request to backend to do some calculations with those 10 files , after calculations there will be response as {imagename: true} or {imagename: false} receiving the response from backend I want to list those 10 images on frontend with a indication that the calculation is true or false.
This is what I tried but I'm stuck after getting response and unable to show the true or false status.
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { Grid, Row, Col } from 'react-flexbox-grid';
import axios, { post } from 'axios';
import compose from 'recompose/compose';
import { withStyles } from '#material-ui/core/styles';
import Styles from '../styles';
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
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 { Scrollbars } from 'react-custom-scrollbars';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
class ImageUploader extends React.Component {
constructor(props) {
super(props);
this.state ={
file:null,
user_name:this.props.username,
checkboxcolor:false,
detailsResoponse:[],
responseList:[],
imageList:[],
uploadResponse:'priya',
a:[],
loaded: 0
}
this.onFormSubmit = this.onFormSubmit.bind(this)
this.handelFile = this.handelFile.bind(this)
this.fileUpload = this.fileUpload.bind(this)
}
onFormSubmit(){
this.setState({
responseList:[]
})
var files=this.state.file
for(var i in files){
this.fileUpload(files[i])
.then((response)=>{
// console.log(response.data);
})
}
}
handelFile(e) {
if(e.target.files.length == 10) {
var self=this
self.setState({
imgList:[],
file:e.target.files,
})
for(var i in e.target.files){
if(i != 'length' && i != 'item'){
if(e.target.files[i].type.split('/')[0] == 'image'){
self.state.imageList.push(e.target.files[i])
}
}
}
}
else{
alert('Please upload 10 images')
}
}
urlBlob(id,file){
var reader = new FileReader();
reader.onload = function (e) {
var image=document.getElementById(id)
image.src=e.target.result
}
reader.readAsDataURL(file);
}
fileUpload(file){
const url = 'http://abc';
const formData = new FormData();
formData.append('image',file)
const config = {
headers: {
'content-type': 'multipart/form-data',
'Access-Control-Allow-Origin':'*'
}
}
return axios.post(url,formData,config)
.then(res => {
var jsondata=JSON.stringify(res.data)
JSON.parse(jsondata, (key, value) => {
// if (value == true) {
// this.state.a.push(key)
var arrayList= this.state.responseList
arrayList.push(res.data)
this.setState({
responseList:arrayList,
// checkboxcolor:true
})
// }
});
}
)
.catch(function (error) {
alert(error)
});
}
render(){
const { classes } = this.props;
console.log(this.state.a)
console.log(this.state.imageList,"yep")
// console.log(this.state.responseList,"responseList")
return (
<div>
<Grid>
<Row>
<Col sm={12} md={12} lg={12}>
<AppBar position="static" color="inherit" className={classes.app}>
<Toolbar>
<Typography variant="title" color="inherit">
Upload Image
</Typography>
</Toolbar>
</AppBar>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Paper elevation={3} style={{padding:20,height:25,marginBottom:20}}>
<input
id="fileItem"
type="file" onChange={this.handelFile}
multiple
/>
<Button color="primary" onClick={this.onFormSubmit}> Upload</Button>
</Paper>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',marginTop:'3%'}}>
<TableHead>
<TableRow >
<TableCell className={classes.phPadding}> Checkbox </TableCell>
<TableCell className={classes.phPadding}> Image </TableCell>
<TableCell className={classes.phPadding}> Name </TableCell>
<TableCell className={classes.phPadding}> Username</TableCell>
<TableCell style={{width:'10%'}}></TableCell>
</TableRow>
</TableHead>
</Table>
<Scrollbars style={{height: 328}}>
{this.state.imageList.map((item,key)=> (
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',borderTop:'0px'}}>
<TableBody>
<TableRow key={key}>
<TableCell className={classes.phPadding}>
{this.state.checkboxcolor ?
<FormControlLabel
control={
<Checkbox
checked={this.state.checkboxcolor}
/>
}
/>
:
null
}
</TableCell>
<TableCell className={classes.phPadding}>
<img id={"image"+key} src={this.urlBlob("image"+key,item)} height="90" width="90" />
</TableCell>
<TableCell className={classes.phPadding}>{item.name}</TableCell>
<TableCell className={classes.phPadding}>{/* {this.state.user_name} */}user_name</TableCell>
<TableCell>
</TableCell>
</TableRow>
</TableBody>
</Table>
))}
</Scrollbars>
</Col>
</Row>
</Grid>
</div>
)
}
}
export default compose(withStyles(Styles))(ImageUploader);
console.log(res.data) response
EDIT: To fix below issue you need to add event.persist() under handleFile function.
Uncaught TypeError: Cannot read property 'files' of null
I updated all the code using ES6.
I noticed some bad practices in your code and corrected them
The way you push elements to state array is not right instead use
previous state to play with array in react state.
Stop using var
instead use let and const
Stop assigning this to self instead use arrow
functions so that you no need to do manual binding and no need to depend on local variable i.e., self
I have corrected your code. This code will show the image responses as you needed
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { Grid, Row, Col } from 'react-flexbox-grid';
import axios, { post } from 'axios';
import compose from 'recompose/compose';
import { withStyles } from '#material-ui/core/styles';
import Styles from '../styles';
import Paper from '#material-ui/core/Paper';
import Button from '#material-ui/core/Button';
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 { Scrollbars } from 'react-custom-scrollbars';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
class ImageUploader extends React.Component {
constructor(props) {
super(props);
this.state ={
file:null,
user_name:this.props.username,
detailsResoponse:[],
responseList:[],
imageList:[],
uploadResponse:'priya',
a:[],
loaded: 0
}
}
onFormSubmit = () => {
this.setState({
responseList:[]
})
let files = this.state.file;
for(var i in files){
this.fileUpload(files[i]);
}
}
handelFile = e => {
e.persist();
if(e.target.files.length == 10) {
this.setState({
imgList:[],
file:e.target.files,
})
for(let i=0; i < e.target.files.length; i++){
if(e.target.files[i].type.split('/')[0] == 'image'){
this.setState(prevState => ({
imageList: [...prevState.imageList, e.target.files[i]]
}));
}
}
}
else{
alert('Please upload 10 images')
}
}
urlBlob = (id,file) => {
let reader = new FileReader();
reader.onload = e => {
let image = document.getElementById(id);
image.src = e.target.result;
}
reader.readAsDataURL(file);
}
fileUpload = file => {
const url = 'http://172.16.92.21:9999';
const formData = new FormData();
formData.append('image',file)
formData.append('uname','150180')
const config = {
headers: {
'content-type': 'multipart/form-data',
'Access-Control-Allow-Origin':'*'
}
}
axios.post(url, formData, config)
.then(res => {
this.setState(prevState => ({
responseList: [...prevState.responseList, res.data]
}));
});
.catch(error => {
alert(error)
});
}
render(){
const { classes } = this.props;
const { responseList } = this.props;
console.log(this.state.a)
console.log(this.state.imageList,"yep")
// console.log(this.state.responseList,"responseList")
const imagesResponse = responseList && responseList.map((image, index) => {
return <li className="list-group-item">{image}</li>
});
return (
<div>
<Grid>
<Row>
<Col>
<ul className="list-group">
{imagesResponse}
</ul>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<AppBar position="static" color="inherit" className={classes.app}>
<Toolbar>
<Typography variant="title" color="inherit">
Upload Image For Surveillance
</Typography>
</Toolbar>
</AppBar>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Paper elevation={3} style={{padding:20,height:25,marginBottom:20}}>
<input
id="fileItem"
type="file" onChange={this.handelFile}
multiple
/>
<Button color="primary" onClick={this.onFormSubmit}> Upload</Button>
</Paper>
</Col>
</Row>
<Row>
<Col sm={12} md={12} lg={12}>
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',marginTop:'3%'}}>
<TableHead>
<TableRow >
<TableCell className={classes.phPadding}> Checkbox </TableCell>
<TableCell className={classes.phPadding}> Image </TableCell>
<TableCell className={classes.phPadding}> Name </TableCell>
<TableCell className={classes.phPadding}> Username</TableCell>
<TableCell style={{width:'10%'}}></TableCell>
</TableRow>
</TableHead>
</Table>
<Scrollbars style={{height: 328}}>
{this.state.imageList && this.state.imageList.map((item,key)=> (
<Table style={{width:'80%',position:'relative',left:'8%',border:'2px solid lightgrey',borderTop:'0px'}}>
<TableBody>
<TableRow key={key}>
<TableCell className={classes.phPadding}>
{responseList ? responseList.map((image, index) => {
return (<FormControlLabel key={index}
control={
<Checkbox
checked={item.name == image.name && image.flag ? true : false }
/>
}
/>)
}): <FormControlLabel
control={
<Checkbox
checked={false}
/>
}
/>
}
</TableCell>
<TableCell className={classes.phPadding}>
<img id={"image"+key} src={this.urlBlob("image"+key,item)} height="90" width="90" />
</TableCell>
<TableCell className={classes.phPadding}>{item.name}</TableCell>
<TableCell className={classes.phPadding}>{/* {this.state.user_name} */}user_name</TableCell>
<TableCell>
</TableCell>
</TableRow>
</TableBody>
</Table>
))}
</Scrollbars>
</Col>
</Row>
</Grid>
</div>
)
}
}
export default compose(withStyles(Styles))(ImageUploader);
Please test from your side and let me know

Categories

Resources