FlexibleWidthXYPlot требует фиксированной ширины для рендеринга

Я использую FlexibleWidthXYPlot внутри компонента, который отображается условно, если компонент должен быть видимым и данные (через реквизиты) определены.

Но react-vis вычисляет ширину диаграммы до width=0 (поэтому диаграмма не отображается). Я могу заставить его правильно рассчитать ширину, перерисовав следующим образом:

    1. Изменить размер браузера
    1. Установите width prop с фиксированным значением.

Я не могу сделать 2. график должен быть отзывчивым. Итак, я попытался вызвать изменение размера на componentDidMount, но тоже безуспешно:

public componentDidMount() {
  window.dispatchEvent(new Event('resize'));
}

Я устанавливаю xDomain вручную, поэтому попробовал dontCheckIfEmpty = {true}, но он тоже не работает ...

Почему FlexibleWidthXYPlot вычисляет ширину по сравнению с 0? И как это исправить?

EconomyOverview.tsx

import React from 'react'
import { Theme, createStyles, withStyles, WithStyles, Card } from '@material-ui/core'
import SelectableTabs from './SelectableTabs'
import PaymentsChart from './PaymentsChart'
import MileageChart from './MileageChart'
import { IMileageChart, IPaymentChart } from '@omnicar/sam-types'
import * as api from 'api/api'
import notify from 'utils/notify/notify'

interface IProps {
  id: string
}

type TProps = IProps & WithStyles<typeof styles>

export type TTab = 'payments' | 'mileage'

interface IState {
  activeTab: TTab
  paymentChartData: IPaymentChart | undefined
  mileageChartData: IMileageChart | undefined
}

const styles = ({ spacing }: Theme) =>
  createStyles({
    root: {
      display: 'flex',
    },
    tabs: {
      width: '35%',
      display: 'flex',
    },
    content: {
      width: '65%',
      padding: spacing.unit * 2,
    },
  })

class ContractDetailsEconomyOverview extends React.Component<TProps, IState> {
  public state: IState = {
    activeTab: 'payments',
    paymentChartData: undefined,
    mileageChartData: undefined,
  }

  public componentDidMount() {
    this.loadData()
  }

  public render() {
    const { classes } = this.props
    const { activeTab, paymentChartData, mileageChartData } = this.state

    return (
      <Card className = {classes.root}>
        <div className = {classes.tabs}>
          <SelectableTabs
            data = {{ paymentChartData, mileageChartData }}
            activeTab = {activeTab}
            onTabSelect = {this.handleTabSelect}
          />
        </div>
        <div className = {classes.content}>
          {activeTab === 'payments' && <PaymentsChart data = {paymentChartData} />}
          {activeTab === 'mileage' && <MileageChart data = {mileageChartData} />}
        </div>
      </Card>
    )
  }
  private handleTabSelect = (activeTab: TTab) => {
    this.setState({ activeTab })
  }

  private loadData = async () => {
    const res = await api.getContractDetailsChartData(this.props.id)

    if (res.errorData || res.networkError) {
      notify.error()
    } else if (res.data) {
      const { paymentChart, mileageChart } = res.data
      this.setState({ paymentChartData: paymentChart, mileageChartData: mileageChart })
    }
  }
}

export default withStyles(styles)(ContractDetailsEconomyOverview)

PaymentsChart

import React, { Component } from 'react'
import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles'
import {
  FlexibleWidthXYPlot,
  YAxis,
  XAxis,
  HorizontalGridLines,
  LineMarkSeries,
  LineSeries,
  DiscreteColorLegend,
  Hint,
} from 'react-vis'
import 'react-vis/dist/style.css'
import { theme } from 'theme'
import { t } from '@omnicar/sam-translate'
import { IPaymentChart, IChartDateValue } from '@omnicar/sam-types'
import { formatDate } from '@omnicar/sam-format'
import Typography from 'components/Typography'
import ChartTooltip from 'components/admin/ChartTooltip'

interface IProps extends WithStyles<typeof styles> {
  data: IPaymentChart
}

interface IState {
  tooltipValue: IChartDateValue | undefined
}

const styles = () =>
  createStyles({
    root: {},
    tooltip: {
      display: 'flex',
    },
    exVat: {
      textAlign: 'right',
      color: theme.palette.text.light,
      fontStyle: 'italic',
    },
    legend: {
      display: 'flex',
      justifyContent: 'space-between',
    },
  })

class PaymentsChart extends Component<IProps, IState> {
  public state: IState = {
    tooltipValue: undefined,
  }
  public legendItems = [
    {
      title: t('Payments'),
      color: '#40A695',
      strokeWidth: 3,
    },
    {
      title: t('Expected payments'),
      color: '#ccc',
      strokeStyle: 'dashed',
      strokeWidth: 3,
    },
    {
      title: t('Costs'),
      color: '#ED7C33',
      strokeWidth: 3,
    },
  ]

  public render() {
    const { classes, data } = this.props
    const { tooltipValue } = this.state
    const curveName = 'curveMonotoneX'

    // transform data
    const earnings = data ? this.transform(data.seriesEarnings) : []
    const costs = data ? this.transform(data.seriesCosts) : []
    const expectedEarnings = data ? this.transform(data.seriesExpectedEarnings) : []

    return (
      <div className = {classes.root}>
        {data && (
          <>
            <DiscreteColorLegend items = {this.legendItems} orientation = "horizontal" />
            <FlexibleWidthXYPlot
              margin = {{ left: 60 }}
              height = {300}
              xType = "time"
              xDomain = {[new Date(data.contractStartDate), new Date(data.contractEndDate)]}
            >
              <YAxis />
              <XAxis />
              <HorizontalGridLines />
              <LineSeries
                data = {expectedEarnings}
                stroke = "#ccc"
                fill = "#ccc"
                strokeWidth = {3}
                strokeStyle = "dashed"
                size = {3}
                curve = {curveName}
              />
              <LineMarkSeries
                data = {costs}
                stroke = "#ED7C33"
                fill = "#ED7C33"
                strokeWidth = {3}
                size = {3}
                curve = {curveName}
                // tslint:disable-next-line jsx-no-lambda
                onValueMouseOver = {(value: IChartDateValue) => {
                  this.setState({ tooltipValue: value })
                }}
                // tslint:disable-next-line jsx-no-lambda
                onValueMouseOut = {() => {
                  this.setState({ tooltipValue: undefined })
                }}
              />

              <LineMarkSeries
                data = {earnings}
                stroke = "#40A695"
                fill = "#40A695"
                strokeWidth = {3}
                size = {3}
                curve = {curveName}
                // tslint:disable-next-line jsx-no-lambda
                onValueMouseOver = {(value: IChartDateValue) => {
                  this.setState({ tooltipValue: value })
                }}
                // tslint:disable-next-line jsx-no-lambda
                onValueMouseOut = {() => {
                  this.setState({ tooltipValue: undefined })
                }}
              />
              {tooltipValue && (
                <Hint value = {tooltipValue}>
                  <ChartTooltip value = {tooltipValue} />
                </Hint>
              )}
            </FlexibleWidthXYPlot>
            <div className = {classes.legend}>
              <div>
                <Typography variant = "subheading">{t('Startdate')}</Typography>
                <Typography variant = "body2">{formatDate(data.contractStartDate)}</Typography>
              </div>
              <div>
                <Typography variant = "subheading">{t('Duration')}</Typography>
                <Typography variant = "body2">{`${data.duration.current} / ${data.duration.total}`}</Typography>
              </div>
              <div>
                <Typography variant = "subheading">{t('Enddate')}</Typography>
                <Typography variant = "body2">{formatDate(data.contractEndDate)}</Typography>
              </div>
            </div>
          </>
        )}
      </div>
    )
  }

  /**
   * Transforms x from string > Date
   */
  private transform = (list: IChartDateValue[]): IChartDateValue[] =>
    list.map(value => ({
      ...value,
      x: new Date(value.x),
    }))
}
export default withStyles(styles)(PaymentsChart)
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
887
1

Ответы 1

У меня сработало добавление реагировать виртуализированный автоматический размер, например:

import React from 'react'
import { makeStyles } from '@material-ui/core/styles'
import { XYPlot, XAxis, YAxis, HorizontalGridLines, LineSeries } from 'react-vis'
import clsx from 'clsx'
import AutoSizer from 'react-virtualized-auto-sizer'

const useStyles = makeStyles({
  plot: {
    height: 300,
    width: '100%'
  },
  hide: {
    display: 'none'
  }
})

function TestChart({ data, hidden }) {
  const classes = useStyles()

  return (
    <div 
      className = {clsx(classes.plot, {
        [classes.hide]: hidden
      })}
    >
      <AutoSizer>
        {({ width, height }) => (
          <XYPlot height = {height} width = {width}>
            <HorizontalGridLines />
            <LineSeries data = {data} />
            <XAxis />
            <YAxis />
          </XYPlot>
        )}
      </AutoSizer>
    </div>
  )
}

export default TestChart

Какими бы ни были ширина и высота родительского div, размер диаграммы автоматически изменится соответствующим образом.

Другие вопросы по теме