【问题标题】:Get data from React props从 React 道具中获取数据
【发布时间】:2020-03-31 14:53:28
【问题描述】:

如何从props 获取数据并确保数据正确到达。我的问题是,在我的类组件中,我收到来自props 的警报,然后我想用警报呈现表格,道具设置异步,我想确保数据到达并且我将能够设置状态。下面是我不太好的尝试解决它,但不起作用(console.log 都显示空数组):

  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 })
    }
  }

整个组件:

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),
);

【问题讨论】:

  • 能否请您显示您传递prophouseAlerts 的组件?
  • 您是否在渲染中尝试过console.log(this.props.householdAlerts)(在返回之前)——只是为了确保householdAlerts 并不总是空数组?
  • 是的,我正在尝试,我在返回之前在渲染方法中获取了数据。
  • 那很好。现在,在您的渲染中,您使用this.props.householdAlerts,但也使用this.state.tableAlerts。对于可能导致错误的警报,您有“两个来源”。你可以选择只使用 props 中的数据吗?
  • 如果您可以共享整个组件的代码可能会更好?无论如何,如果我是你 - 将检查 this.setAlertForTable() 以及与之相关的所有内容。也许此方法使用警报为空数组的初始状态?

标签: javascript reactjs setstate


【解决方案1】:

在构造函数中为 state 设置初始值是不好的做法,因此使用 lifeCycles 作为 componentDidMount 或 componentDidUpdate 来操作状态。


 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...
}

现在在渲染时,{ this.state.tableAlerts } 应该包含正确的信息

【讨论】:

  • 为什么在构造函数中设置初始属性是不好的做法?在 React 文档中,它建议在那里做:reactjs.org/docs/react-component.html#constructor
  • 如果 state 依赖于 props,当 props 改变时 state 不会自动更新。这就是为什么我们会在某些组件生命周期方法中监听 props 的变化,并在 props 变化时更新状态。
【解决方案2】:

您可能应该使用componentDidUpdate。它在每个状态和道具更改上运行。

这是一个如何使用它的示例:

componentDidUpdate(prevProps) { 
  if(this.props.householdAlerts !== prevProps.householdAlerts) { 
    this.setState({ tableAlerts: this.props.householdAlerts }) 
  } 
}

【讨论】:

  • 但是怎么做呢?我刚刚包含了我的 componentDidUpdate 并且有空数组;/
  • componentDidUpdate(prevProps) { if(this.props.householdAlerts !== prevProps.householdAlerts) { this.setState({ tableAlerts: this.props.householdAlerts }) } }
猜你喜欢
  • 2019-11-05
  • 1970-01-01
  • 2019-04-16
  • 2020-09-28
  • 1970-01-01
  • 2018-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多