Событие щелчка не обновляется в React / D3 при обновлении данных

#reactjs #d3.js #mouseevent

#reactjs #d3.js #mouseevent

Вопрос:

У меня есть компонент React, который отображает гистограмму D3, где столбцы доступны для просмотра. Он работает должным образом, пока родительское состояние не будет обновлено. Столбцы обновляются правильно, но события щелчка на столбцах по-прежнему привязаны к предыдущим данным. Я не уверен, что это то, что я делаю неправильно в React или D3 или обоих. Любая помощь приветствуется.

 const DATA1 = [{"value":1}, {"value":1}]

const DATA2 = [{"value":3}, {"value":3}, {"value":3}, {"value":2}, {"value":2}]

const CLICK = 'chartClick'

function handleClick(data, i, els) {
  const element = els[i]

  const event = new Event(CLICK, {bubbles: true, detail: data})

  return element.dispatchEvent(event)
}

class D3Chart {
  constructor(selector) {
    this.svg = d3.select(selector).append('svg')
  }

  draw(data) {
    const xScale = d3.scaleLinear()
      .domain([0, d3.max(data.map(i => i.value))])
      .range([0, 500])

    const barGroups = this.svg.selectAll('.barGroup').data(data)

    const barGroupsEnter = barGroups.enter().append('g')
      .attr('class', 'barGroup')
      .attr('transform', (d, i) => {
        const y = (i * 25)

        return `translate(0, ${y})`
      })

    barGroupsEnter.append('rect')
      .attr('class', 'bar')
      .attr('height', 20)
      .attr('width', d => xScale(d.value))
      .on('click', handleClick)

    barGroups.exit().remove()
  }
}


class Chart extends React.Component {
  chartRef = React.createRef()

  componentDidMount() {
    const {data, onClick} = this.props

    this.chartRef.current.addEventListener(CLICK, onClick)

    this.chart = new D3Chart(this.chartRef.current)

    this.chart.draw(data)
  }

  shouldComponentUpdate(nextProps) {
    const {data} = this.props

    return !_.isEqual(data, nextProps.data)
  }

  componentDidUpdate() {
    const {data} = this.props

    this.chart.draw(data)
  }

  render() {
    return <div ref={this.chartRef}></div>
  }
}


class App extends React.Component {
  state = {data: DATA1}

  handleButtonClick = () => this.setState({data: DATA2})

  handleChartClick = (data, event) => console.log('data length on click', data.length)

  render() {
    const {data} = this.state

    console.log('data length on render', data.length)

    return (
      <React.Fragment>
        <button onClick={this.handleButtonClick}>Update Data</button>
        <Chart data={data} onClick={(event) => this.handleChartClick(data, event)} />
      </React.Fragment>
    )
  }
}

ReactDOM.render(<App />, document.querySelector('.root'))
 

Вывод при первоначальном рендеринге / щелчке:

 "data length on render" 2
"data length on click" 2
 

Вывод после обновления данных:

 "data length on render" 5
"data length on click" 2
 

Я ожидаю, что последнее будет:

 "data length on render" 5
"data length on click" 5
 

Пример Codepen здесь: https://codepen.io/bohmanart/pen/QPQJdX

Ответ №1:

Вы можете попробовать приведенное ниже решение, изменить событие onclick внутри диаграммы на this.chartRef.current.addEventListener(CLICK, ()=>onClick(this.props.data.length);) , изменить реквизит onClick на диаграмме на onClick={this.handleChartClick} , а затем изменить дескриптор диаграммы нажмите на handleChartClick = (data) =>{ console.log('data length on click', data);}

Я не уверен в том, почему вы хотите передать данные из компонента диаграммы handleChartClick , когда они уже доступны в состоянии класса приложения. Вы можете просто использовать this.state.data.length в handleChartClick

Комментарии:

1. Спасибо, что взглянули. Я попробовал то, что вы предложили, но я все равно получаю те же результаты. Новая ручка кода: codepen.io/bohmanart/pen/wZmvGQ

2. Можете ли вы поместить () => onClick(this.props.data) вместо этого, если просто данные в добавить прослушиватель событий диаграммы. Поскольку прослушиватель события добавления находится в componentDidMount, и вы уничтожили данные в самый первый раз, это значение данных будет передано для последующих вызовов onClick. Поэтому вам нужно обратиться к this.props.data, чтобы получить последнее значение.

3. Хорошо, теперь это имеет смысл, когда вы указали на это. Я обновил codepen, и это сработало! Мои попытки выяснить это в течение нескольких недель были тщетными. Спасибо!

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