Гистограмма Nivo вызывает функцию метки сотни раз

Я использую панель Nivo, чтобы показать прогресс пользователя в бюджете. Я нормализовал данные, разделив баланс категорий на цель категории. Пример данных.

[{
    "category": "Gas",
    "budget": 0.24,
    "over_budget": 0.0
},
{
    "category": "Groceries",
    "budget": 1.0,
    "over_budget": 0.26
}]

Я не хочу, чтобы эти значения использовались в качестве метки на диаграмме. Я планирую использовать в качестве метки фактическое значение баланса. У меня есть конечная точка, которая вернет баланс для категории, и я попытался использовать это значение следующим образом:

<ResponsiveBar
...
label={d => this.getDollarAmount(d.value)}
...
>

С функцией POC как:

getDollarAmount(value) {
    console.log("hitting getDollarAmount")
    return 1
  };

Сообщение журнала регистрируется более 500 раз. Я ожидал, что функция будет задействована только один раз для каждого бара на графике.

Я все еще учусь реагировать, так что это может быть что-то очевидное. Заранее спасибо!

РЕДАКТИРОВАТЬ - вот весь компонент BarChart:

import axios from 'axios';

import React, { Component } from "react";

import { ResponsiveBar } from '@nivo/bar'

// Nivo theming
const theme = {
  axis: {
    ticks: {
      line: {
        stroke: "#e9ecee",
        strokeWidth: 40
      },
      text: {
        // fill: "#919eab",
        fill: "black",
        fontFamily: "BlinkMacSystemFont",
        fontSize: 16
      }
    }
  },
  grid: {
    line: {
      stroke: "#e9ecee",
      strokeWidth: 5
    }
  },
  legends: {
    text: {
      fontFamily: "BlinkMacSystemFont"
    }
  }
};

let budgetStatusAPI = 'http://127.0.0.1:8000/api/budget_status/?auth_user=1&month=2020-02-01';

class BarChart extends Component {

  constructor(props) {
    super(props);

    this.state = {
      data: [],
    }

    this.getDollarAmount = this.getDollarAmount.bind(this);
  }


  componentDidMount() {
    console.log("component did mount")

    axios.get(budgetStatusAPI).then(response => {
      this.setState({
        data: response.data
      }, function () {
        console.log(this.state.data);
      })
    });
  }

  componentDidUpdate() {
    console.log("component did update")
  }

  getDollarAmount(value) {
    console.log("hitting getDollarAmount")
    console.log(value)
    return 1
  };


  render() {

    const hard_data = [
        {
          "category": "Groceries",
          "budget_status": 1.0,
          "over_budget": .26,
        },
        {
          "category": "Gas",
          "budget_status": .24,
          "over_budget": 0.0,
        }]

    return(

      <ResponsiveBar
        maxValue={1.5}
        markers={[
            {
                axis: 'x',
                value: 1,
                lineStyle: { stroke: 'rgba(0, 0, 0, .35)', strokeWidth: 2 },
                legend: 'Goal',
                legendOrientation: 'horizontal',
                legendPosition: 'top'
            },
        ]}
        enableGridX={false}
        gridXValues={[1]}
        enableGridY={false}
        data={this.state.data}
        // data={hard_data}
        keys={['budget_status', 'over_budget']}
        indexBy="category"
        margin={{ top: 25, right: 130, bottom: 50, left: 125 }}
        padding={0.3}
        layout="horizontal"
        colors={{ scheme: 'set2' }}
        theme={theme}
        defs={[
            {
                id: 'dots',
                type: 'patternDots',
                background: 'inherit',
                color: '#38bcb2',
                size: 4,
                padding: 1,
                stagger: true
            },
            {
                id: 'lines',
                type: 'patternLines',
                background: 'inherit',
                color: '#eed312',
                rotation: -45,
                lineWidth: 6,
                spacing: 10
            }
        ]}
        borderColor={{ from: 'color', modifiers: [ [ 'darker', 1.6 ] ] }}
        axisBottom={null}
        label={d => this.getDollarAmount(d.value)}
        isInteractive={false}
        animate={true}
        motionStiffness={90}
        motionDamping={15}
    />
    )
  }
}


export default BarChart;

Воспроизведено здесь: https://codesandbox.io/s/nivo-bar-label-issue-k4qek


person brewcrazy    schedule 14.03.2020    source источник
comment
В вашем коде комм. (или другого управления состоянием приложения) есть что-то, что запускает несколько отрисовок. Пожалуйста, предоставьте полный код компонента.   -  person Christos Lytras    schedule 15.03.2020
comment
Я добавил весь компонент. Я заметил, что когда я использую жестко запрограммированные значения для своих данных, этой проблемы не возникает. Если я использую данные, полученные из состояния, это происходит.   -  person brewcrazy    schedule 16.03.2020
comment
Я воспроизвел это здесь: codeandbox.io/s/nivo-bar-label- issue-k4qek все еще не уверен, почему это происходит.   -  person brewcrazy    schedule 20.03.2020
comment
Пожалуйста, проверьте мой ответ ниже.   -  person Christos Lytras    schedule 21.03.2020


Ответы (1)


Многократный вызов происходит из-за того, что гистограмма вызывает функцию label для каждого рендеринга тика / кадра анимации. Если мы настроим счетчик, мы увидим, что с animate prop, установленным на true, он будет отображать от 450+ до 550+ раз, но если мы установим prop animate на false, мы будем отображать 6 раз, что точно соответствует количеству значений цен. > 0.0.

Если вы хотите избежать всех этих визуализаций, вам придется отключить анимацию, используя свойство animate={false} следующим образом:

getDollarAmount(value) {
  // Remove every console.log inside this function

  return `$${value}`
}

render() {
  return (
    <ResponsiveBar
      animate={false}
      label={d => this.getDollarAmount(d.value)}
      ...
  );
}

Вы можете проверить его работу в клонированном CodeSandbox. Я установил animate на false, а журнал counter внутри getDollarAmount вызывает 6 раз. Попробуйте изменить animate на true, и вы увидите рендеры 500+-.

Кроме того, вам не нужно создавать функцию для каждого label вызова, вы можете просто передать функцию getDollarAmount и позволить ей обрабатывать весь параметр d следующим образом:

getDollarAmount(d) {
  // Remove every console.log inside this function

  return `$${d.value}`
}

render() {
  return (
    <ResponsiveBar
      animate={false}
      label={this.getDollarAmount}
      ...
  );
}
person Christos Lytras    schedule 21.03.2020
comment
Как я узнаю, что он все еще не вызывает функцию сотни раз? - person brewcrazy; 21.03.2020
comment
@brewcrazy ты прав, виновата не это, анимация есть. Проверьте мой обновленный ответ. - person Christos Lytras; 21.03.2020