plotly / plotly/react-plotly.js

uirevision not preserving the trace's visibility (legend click)

Open
#171 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
1.1k
Forks
138
Avg merge
3d 2h
Merged PRs (30d)
4

Description

When adding the below line to the layout [1], one would expect that the chart preserves the trace's visibility even after the data is reload, or other elements of a webapp change.

Whilst it works nice and clean in a simple test, in an almost empty react project, it doesn't seem to work well in a medium sized app, with dozens of Components and states.

In my specific example, it is enough for someone to change the navigation bar dropdowns, which trigger an event changing the state of App.js, where the charts originate, too. In the very same second when the dropdown changes its value, the charts lose their 'memory' of what was previously selected, and revert back to their original state.

I'll see if I can reproduce this in a sharable code-pen, but perhaps someone already encountered this issue?

Am using react-plotly 2.4.0 with plotly.js 1.48.0

**1**
uirevision: 'true'

Was able to recreate it. ui revision seems to be failing when callbacks are used in reacts setState.
Clicking 'Button2' twice, will reset the selected traces to their default. Simply removing the callback (which does not affect the plot) makes it behave as intended again.

import React, { Component } from 'react';
import './App.css';
import MyPlot from "./plotly_test";
import NavbarTop from "./NavbarTop";


class App extends Component {
  constructor() {
    super();
    this.onClickButton = this.onClickButton.bind(this);
    this.onClickButton2 = this.onClickButton2.bind(this);
    this.state = {
      myID: 1,
      countries: {names: ['Alal', 'lqwel', 'qpweok'], labels: ['loqwe', 'qwoel', 'qlweo']},
      country: 'Poland',
    }
  };


  onClickButton() {
    this.setState({myID: this.state.myID + 1})
    this.changeCountry = this.changeCountry.bind(this);
    var labels = this.state.countries.labels
    this.setState({
      labels: labels.push('SomeLabel')
    })
  }

  onClickButton2() {
    this.setState({
      country: "SomeCountry",
    }, ()=>{
      this.setState({
        country: 'Yet Another'
      })
    })
  }

  changeCountry = (value, callback) => {
    this.setState({
      country: value,
    }, () => {
      callback(value)
    })
  };

  getCharts() {
    let components = [];
    components.push((<MyPlot id={this.state.myID}/>));
    return components
  }

  render() {
    let charts = this.getCharts();

    return (
      <div>
        <h1>{this.state.country}</h1>
        <NavbarTop callbackChange={this.changeCountry}/>

        <div className="App">
          {charts}
          <button
            onClick={this.onClickButton}
          > Button1
          </button>
          <button
            onClick={this.onClickButton2}
          > Button2
          </button>
        </div>
      </div>
    )
  }
}

export default App;

Navbar:

import React, { Component } from 'react';

class NavbarTop extends Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    this.props.callbackChange(event.target.value, (something) => {
      console.log(something);
    })
  }

  render() {
    return (
      <div>
        <nav>
          <nav>
            <select name="countries" id="countries"
                    onChange={this.handleChange} value={'PL'}>
              <option value='DE'>Germany</option>
              <option value='FR'>France</option>
              <option value='ES'>Spain</option>
              <option value='UK'>United Kingdom</option>
            </select>
          </nav>
        </nav>
      </div>
    );
  }
}

export default NavbarTop;

plotly_test:


import React, {Component} from 'react'
import Plot from 'react-plotly.js'

class MyPlot extends Component {

  render() {
    const rand = () => 1;
    var x = [1, 2, 3, 4, 5];
    const new_data = (trace) => Object.assign(trace, {y: x.map(rand)});

// add random data to three line traces
    var data = [
      {mode:'lines', line: {color: "#b55400"}},
      {mode: 'lines', line: {color: "#393e46"}},
      {mode: 'lines', line: {color: "#222831"}}
    ].map(new_data);

    var layout = {
      title: this.props.id,
      uirevision:'true',
      xaxis: {autorange: true},
      yaxis: {autorange: true}
    };

    return (
      <div>
        <Plot
        layout={layout}
        data={data}
        />
      </div>
    );
  }
}

export default MyPlot;

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied App component and the plotly_test and NavbarTop components, then reproduce the issue by clicking a legend item and triggering Button2's callback-based setState updates. Trace how react-plotly.js receives the updated props and determine why uirevision stops preserving visibility; done means selected trace visibility remains unchanged after those updates.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react
Domain
data-visualization, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.