Victory: VictoryVoronoiContainer sometimes crashing when triggering hover w/ tooltip

Created on 16 Jul 2019  路  4Comments  路  Source: FormidableLabs/victory

Checklist

  • [x ] This is not a victory-native specific issue. (Issues that only appear in victory-native should be opened here)

  • [x ] I have read through the FAQ and Guides before asking a question

  • [x ] I am using the latest version of Victory

  • [x ] I've searched open issues to make sure I'm not opening a duplicate issue

The Problem

Hi,

The issue is quite similar to #1125 (which was closed as not seen for a while), and I suspect somewhat similar to #1314 although symptoms differ a little bit.

I'm using VictoryVoronoiContainer with a VictoryTooltip. In general it's working correctly, but sometimes after refreshing / altering the charts data and triggering some tooltip at the same time with mouse hover, some props seem to be corrupted, hence crashing:

TypeError: scale.y is not a function helpers.js:92
    scalePoint helpers.js:92
    getLabelPosition victory-voronoi-container.js:252
    getLabelProps victory-voronoi-container.js:370
    getTooltip victory-voronoi-container.js:385
    getChildren victory-voronoi-container.js:394
    renderContainer victory-container.js:205
    render victory-container.js:275

I've added some debug output to show data corruption (this is the props parameter from there: https://github.com/FormidableLabs/victory/blob/master/packages/victory-voronoi-container/src/victory-voronoi-container.js#L129 ) :

  • When data is fine:
    not corrupted

  • When data is corrupted:
    corrupted

Full dump: https://paste.fedoraproject.org/paste/inAY8DBJBzG1GjWN32kyuw

See how most props are being filled with the same object data: { active: true } ? This is the case for activateData and activateLabels which should be booleans, or height which should be a number, etc. Also I've noticed a new field named 3 (or whatever other number) that I guess shouldn't be here.

Reproduction

Unfortunately I haven't been able to isolate a reproducer. I can reproduce many times when used within my project, but for some reason I could not reproduce within a smaller context such as storybook. So that might be tricky to recreate the right context, but I hope I'm giving enough clues above to help.

Most helpful comment

Hi @boygirl

I've been debugging a lot since yesterday. I haven't found the exact problem but I'm getting closer, and also I've a workaround that works for me.

I've noticed that the crash occurs specifically when in this line of code voronoi-helpers / getDatasets() / iteratee, the name of the child is parent. I don't know why, under some circumstances, that name is "parent" ; it's not usual, usually it's something like chart-group-0-area-0. But it seems that when the charts are being reloaded, sometimes, it can be parent.

While looking at the code, I saw that there's a blacklisting mechanism to ignore some children. So, that's my workaround: in voronoi container props I set voronoiBlacklist={['parent']}. But this is certainly not fixing the root cause.

While looking at the props, we can see that there seem to be an event fired on behalf of "parent", and I believe the error comes from there. We can see it in the dump I posted yesterday , line 4457.

I guess this is kind of a racy condition that happens on redrawing.

What is still obscure to me, is why in such situation all the props are corrupted in the end, all properties seeming to be spread with the object data: { active: true }. This seems to be a common pattern in victory to perform a mutation with { active: true }, but I'm not familiar enough with victory, at this point I prefer to let you / team try to see how it could corrupt the whole props :-)

All 4 comments

PS: Here's how I create a container:

export const createContainer = () => {
  const tooltip = (
    <VictoryTooltip
      style={{ stroke: 'none', fill: 'white' }}
      renderInPortal={true}
    />
  );
  return (
    <VictoryVoronoiContainer
      labels={dp => `${dp.name}: ${getFormatter(d3Format, dp.unit)(dp.y)}`}
      labelComponent={tooltip}
    />
  );
};

And how I'm creating charts:

    return (
      <div ref={this.containerRef}>
        <TextContent>
          <Text component={TextVariants.h4} style={{textAlign: 'center'}}>{this.props.chart.name}</Text>
        </TextContent>
        <div className="area-chart-overflow">
          <VictoryChart
            height={height}
            width={this.state.width}
            containerComponent={createContainer()}
            themeColor={ChartThemeColor.multi}
            scale={{x: 'time', y: 'linear'}}
          >
            <VictoryGroup>
              {this.props.data.series.map((line, idx) => {
                return (<VictoryArea key={'line-' + idx} data={line} />);
              })}
            </VictoryGroup>
            <VictoryAxis
              tickCount={scaleInfo.count}
              style={{ tickLabels: {fontSize: 12, padding: 2} }}
            />
            <VictoryAxis
              tickLabelComponent={<VictoryPortal><VictoryLabel/></VictoryPortal>}
              dependentAxis={true}
              tickFormat={getFormatter(d3Format, this.props.chart.unit)}
              style={{ tickLabels: {fontSize: 12, padding: 2} }}
            />
          </VictoryChart>
        </div>
        <ChartLegend
          x={50}
          data={legend.items}
          height={legend.height}
          themeColor={ChartThemeColor.multi}
          width={this.state.width}
          itemsPerRow={legend.itemsPerRow}
        />
      </div>
    );

@jotak thank you for the issue. It looks like the event state object is somehow overriding the props. I haven't seen this happen before. Can you try supplying your containerComponent inline instead from the function you are importing? It _shouldn't_ make a difference, but it can be helpful to simplify as much as possible. Along those lines, the next thing to try might be to remove the VictoryGroup wrapper from the VictoryArea components you are rendering. Let me know if either of these things helps.

Hi @boygirl

I've been debugging a lot since yesterday. I haven't found the exact problem but I'm getting closer, and also I've a workaround that works for me.

I've noticed that the crash occurs specifically when in this line of code voronoi-helpers / getDatasets() / iteratee, the name of the child is parent. I don't know why, under some circumstances, that name is "parent" ; it's not usual, usually it's something like chart-group-0-area-0. But it seems that when the charts are being reloaded, sometimes, it can be parent.

While looking at the code, I saw that there's a blacklisting mechanism to ignore some children. So, that's my workaround: in voronoi container props I set voronoiBlacklist={['parent']}. But this is certainly not fixing the root cause.

While looking at the props, we can see that there seem to be an event fired on behalf of "parent", and I believe the error comes from there. We can see it in the dump I posted yesterday , line 4457.

I guess this is kind of a racy condition that happens on redrawing.

What is still obscure to me, is why in such situation all the props are corrupted in the end, all properties seeming to be spread with the object data: { active: true }. This seems to be a common pattern in victory to perform a mutation with { active: true }, but I'm not familiar enough with victory, at this point I prefer to let you / team try to see how it could corrupt the whole props :-)

PS: I'm happy enough with the workaround at the moment but I can help to test if you have potential fixes to try.

Was this page helpful?
0 / 5 - 0 ratings