Reputation: 3139
How to get data from props
and be sure that data arrived properly. My issue is that in my class component I receive alarms from props
and then I want to render table with the alarms, props sets asynchronously and I want to be sure that the data arrived and that I will be able to set a state. Below is my not nice try to solve it, but does not work (both console.log
shows empty array):
componentDidMount() {
this.setState({
startDate: moment()
.subtract(30, 'days')
.startOf('day'),
endDate: moment().endOf('day'),
selectedSeverity: null,
isChecked: true,
});
if(this.state.tableAlerts.length === 0){
console.log('tableAlerts', this.state.tableAlerts)
console.log('householdAlerts', this.props.householdAlerts)
this.setState({ tableAlerts: this.props.householdAlerts })
}
}
The whole component:
import React, { useState } from 'react';
import T from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { moment } from 'react-moment';
import { t } from 'i18next';
import _ from 'lodash';
import { TimezoneLabel } from 'cloud-modules-user/build/TimezoneLabel';
import { Table, Radio } from 'semantic-ui-react';
import { RangeDatePicker } from 'cloud-modules-user/build/RangeDatePicker';
import { SeverityFilter } from 'cloud-modules-user/build/Alerts';
import { fetchHouseholdAlerts } from '../Redux/Household.actions';
const alertSeverityText = ({ severity }) =>
severity < 3 ? 'error' : 'warning';
const alertsBySeverity = alerts => {
const groupedAndCounted = _.countBy(alerts, alertSeverityText);
return severity => groupedAndCounted[severity] || 0;
};
const alertSeverityColor = {
error: '#e05567',
warning: '#f9b233',
};
export class HouseholdAlertsPure extends React.Component {
constructor(props) {
super(props);
this.state = {
tableAlerts: props.householdAlerts
}
}
static propTypes = {
householdAlerts: T.arrayOf(T.object).isRequired,
};
componentDidMount(prevProps) {
this.setState({
startDate: moment()
.subtract(30, 'days')
.startOf('day'),
endDate: moment().endOf('day'),
selectedSeverity: null,
isChecked: true,
});
console.log('prevprops', prevProps)
// if(this.props.householdAlerts !== prevProps.householdAlerts){
// this.setState({ tableAlerts: this.props.householdAlerts })
// }
// if(this.state.tableAlerts.length === 0){
// console.log('tableAlerts', this.state.tableAlerts)
// console.log('householdAlerts', this.props.householdAlerts)
// this.setState({ tableAlerts: this.props.householdAlerts })
// }
}
onChangeDate = e => {
const startDate = moment(e.startDate).startOf('day');
const endDate = moment(e.endDate).endOf('day');
this.setState({ startDate, endDate });
const {
// eslint-disable-next-line no-shadow
fetchHouseholdAlerts,
match: { params },
} = this.props;
fetchHouseholdAlerts(params.deviceGroupId, startDate, endDate);
};
selectOngoingAlerts = (e, data) => {
const { isChecked } = this.state;
this.setState( {isChecked : !isChecked} );
const { householdAlerts } = this.props;
// check whether alarm is ongoing
if(isChecked){
// filter ongoing alerts
let filteredHouseholdAlerts = householdAlerts.filter((alert) => alert.setTimestamp < alert.clearTimestamp)
this.setState( {tableAlerts: filteredHouseholdAlerts} )
}else{
// show all alerts, without any filter
this.setState({tableAlerts: householdAlerts});
}
}
handleOnChangeSeverity = (e, selectedOption ) => {
console.log('e', e)
this.setState( {selectedSeverity: e.option} )
console.log('selectedSeverity', this.state.selectedSeverity)
};
setAlertForTable = () => {
// if(this.state.alertsInitialised == true) return;
console.log('setAlertForTable')
// this.setState ( {alertsInitialised: true} )
}
render() {
console.log('test', this.props.householdAlerts)
const { startDate, endDate } = this.state;
const datesInitialized = startDate && endDate;
// The dates are not set until the component is mounted.
if (!datesInitialized) return null;
const { householdAlerts } = this.props;
const labels = {
from: t('householdAlertsTimeRangeFrom'),
to: t('householdAlertsTimeRangeTo'),
};
const numberOfAlert = alertsBySeverity(householdAlerts);
return (
<div className="alert-table">
<section className="alerts-buttons">
<div className="ongoing-box">
<Radio toggle className="ongoing-checkbox"
label="Ongoing alerts"
value={ !this.state.isChecked }
onChange={this.selectOngoingAlerts}
></Radio>
</div>
<SeverityFilter className="severity--button"
onChange={this.handleOnChangeSeverity}
value={this.state.selectedSeverity}
option={this.state.selectedSeverity || 'all'}
></SeverityFilter>
{ this.setAlertForTable() }
<div className="time-range">{t('householdAlertsTimeRange')}</div>
<RangeDatePicker
id="rangeDateSelector"
labels={labels}
onChange={e => this.onChangeDate(e)}
selectedDateRange={[startDate, endDate]}
filterDate={date => moment() > date}
/>
</section>
<div className="alert-table--statuses">
<div aria-label="error">
<div
className="alert-table--statuses-item"
style={{ backgroundColor: alertSeverityColor.error }}
/>
<span className="alert-table--statuses-item-number">
{numberOfAlert('error')}
</span>
</div>
<div aria-label="warning">
<div
className="alert-table--statuses-item"
style={{ backgroundColor: alertSeverityColor.warning }}
/>
<span className="alert-table--statuses-item-number">
{numberOfAlert('warning')}
</span>
</div>
</div>
<Table sortable>
<Table.Header>
<Table.Row>
<Table.HeaderCell style={{ width: '116px' }}>
{t('householdAlertsSeverity')}
</Table.HeaderCell>
<Table.HeaderCell textAlign="left">
{t('householdAlertsDevice')}
</Table.HeaderCell>
<Table.HeaderCell textAlign="left">
{t('householdAlertsAlertName')}
</Table.HeaderCell>
<Table.HeaderCell textAlign="left">
{t('householdAlertsAlertsMessage')}
</Table.HeaderCell>
<Table.HeaderCell textAlign="right">
{t('householdAlertsTimestamp')}
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{householdAlerts && householdAlerts.length !== 0 ? (
_.map(
householdAlerts,
({
id,
severity,
deviceName,
name,
setMessage,
setTimestamp,
}) => (
<Table.Row key={id}>
<Table.Cell
className="alert-table--column-severity"
textAlign="right"
>
<div
className="alert-table--column-severity__status"
style={{
backgroundColor:
alertSeverityColor[alertSeverityText({ severity })],
}}
/>
</Table.Cell>
<Table.Cell
className="alert-table--column-device"
textAlign="left"
>
{deviceName}
</Table.Cell>
<Table.Cell
className="alert-table--column-alert"
textAlign="left"
>
{name}
</Table.Cell>
<Table.Cell
className="alert-table--column-message"
textAlign="left"
>
{setMessage}
</Table.Cell>
<Table.Cell
className="alert-table--column-timestamp"
textAlign="right"
>
{moment(setTimestamp).format('DD-MM-YYYY HH:mm')}
</Table.Cell>
</Table.Row>
),
)
) : (
<Table.Row>
<Table.Cell colSpan={7}>
{t('householdAlertsMessageNoAlerts')}
</Table.Cell>
</Table.Row>
)}
</Table.Body>
</Table>
<section className="timezone-wrapper">
<TimezoneLabel />
</section>
</div>
);
}
}
const mapStateToProps = state => ({
householdAlerts: state.HouseholdReducer.householdAlerts,
});
const mapDispatchToProps = { fetchHouseholdAlerts };
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(HouseholdAlertsPure),
);
Upvotes: 0
Views: 1197
Reputation: 1864
You should probably use componentDidUpdate
. It runs on each state and prop change.
This is an example of how it can be used:
componentDidUpdate(prevProps) {
if(this.props.householdAlerts !== prevProps.householdAlerts) {
this.setState({ tableAlerts: this.props.householdAlerts })
}
}
Upvotes: 1
Reputation: 2877
It is bad practice to set initial values to state at the constructor, therefore use lifeCycles as componentDidMount or componentDidUpdate instead to manipulate the state.
export class HouseholdAlertsPure extends React.Component {
constructor(props) {
super(props);
// initial values for state
this.state = {
tableAlerts: [],
startDate: null,
endDate: null,
selectedSeverity: null,
isChecked: false
}
}
componentDidMount() {
// values declared for state at component first load
this.setState({
startDate: moment()
.subtract(30, 'days')
.startOf('day'),
endDate: moment().endOf('day'),
selectedSeverity: null,
isChecked: true,
tableAlerts: this.props.householdAlerts
});
componentDidUpdate() {
// called whenever prop value changed
if(this.props.householdAlerts !== this.state.tableAlerts)
this.setState({ tableAlerts: this.props.householdAlerts })
}
selectOngoingAlerts = (e, data) => {
const { isChecked, tableAlerts } = this.state;
this.setState( {isChecked : !isChecked} );
// right now, { isChecked } still refer to construct abouve methond and prev value...
// for more consist aproach, consider do the filtering logic direct at render without manipulate state
if(!isChecked) {
this.setState( { tableAlerts: tableAlerts.filter((alert) => alert.setTimestamp < alert.clearTimestamp)} )
} else { this.setState({tableAlerts}) }
}
...rest class...
}
Now at render, { this.state.tableAlerts } should contain the correct information
Upvotes: 1