繁体   English   中英

D3工具提示未在React组件中正确更新

[英]D3 tooltip not updating correctly in a React Component

我花了很长时间制作这段代码片段,以突出一个非常令人沮丧的问题,我遇到了使用d3并一起做出反应,特别是尝试根据组件状态动态更新工具提示。

首先,代码片段,我尽力保持简短和完整(为了简洁起见,您可以跳过css。只需注意单选按钮组,鼠标悬停在图形组件中):

 class GraphComponent extends React.Component { constructor(props) { super(props); } // Lifecycle Components drawGraph() { const { buttonName } = this.props; const myData = [ {x:25, y:30, name:"tommy", age:"24"}, {x:108, y:82, name:"joey", age:"21"}, {x:92, y:107, name:"nicky", age:"23"}, {x:185, y:50, name:"peter", age:"27"}, {x:65, y:80, name:"mickie", age:"4"}, {x:165, y:80, name:"gregie", age:"14"}, {x:154, y:10, name:"tammie", age:"24"}, {x:102, y:42, name:"benny", age:"29"} ] // var myD3tip = d3Tip() var myD3tip = d3.tip() .attr('class', 'd3-tip') .offset([-20,0]) .html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`) console.log('buttonName outside mouseover', buttonName) const divToolTip = d3.select('div#myTooltip') const points = d3.select('#mySvg').select('g.points') points.call(myD3tip) points .selectAll('circle') .data(myData) .enter() .append('circle') .attr('cx', d => dx) .attr('cy', d => dy) .attr('r', 8) .attr('fill', 'blue') .on('mouseover', function(d) { myD3tip.show(d) console.log('buttonName in mouseover', buttonName) divToolTip .style('opacity', 1) .style('left', d3.mouse(this)[0] + 'px') .style('top', (d3.mouse(this)[1]) + 'px') .html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`) }) .on('mouseout', function(d) { myD3tip.hide(d) divToolTip .style('opacity', 0) }) } componentDidUpdate() { this.drawGraph() } componentDidMount() { this.drawGraph() } render() { return ( <svg id="mySvg"> <g className="points" /> </svg> ) } } class GraphContainer extends React.Component { constructor(props) { super(props); this.state = { ageOrName: "age" } this.handleChange = this.handleChange.bind(this); } handleChange = (event) => { this.setState({ ageOrName: event.target.value }) } render() { const { ageOrName } = this.state; const ageOrNameOptions = ["age", "name"]; const ageOrNameButtons = <form> <div> {ageOrNameOptions.map((d, i) => { return ( <label key={i+d}> <input type={"radio"} value={ageOrNameOptions[i]} checked={ageOrName === ageOrNameOptions[i]} onChange={this.handleChange} /> <span>{ageOrNameOptions[i]}</span> </label> ) })} </div> </form>; return ( <div> {ageOrNameButtons} <GraphComponent buttonName={ageOrName} /> </div> ) } } ReactDOM.render( <GraphContainer />, document.getElementById('root') ); 
 /* Div ToolTip */ #myTooltip { opacity: 0; position: absolute; pointer-events: none; background-color: lightblue; line-height: 0.50; font-weight: bold; padding: 8px 8px; padding-bottom: 0px; border-radius: 10px; border: 2px solid #444; font-size: 0.9em; } /* ========== */ /* D3 ToolTip */ /* ========== */ .d3-tip { line-height: 0.50; font-weight: bold; padding: 8px 8px; padding-bottom: 0px; border-radius: 10px; border: 2px solid #444; font-size: 0.9em; } /* Creates a small triangle extender for the tooltip */ .d3-tip:after { box-sizing: border-box; display: inline; font-size: 10px; width: 100%; line-height: 1; color: #444; content: "\\25BC"; position: absolute; text-align: center; } /* Style northward tooltips differently */ .d3-tip.n:after { margin: -1px 0 0 0; top: 100%; left: 0; } /* ========== */ 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script> <script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script> <div id="root"> <svg id="mySvg"> </div> <div id="myTooltip"></div> 

概述如下:

我有2个组件,一个容器组件和一个图形组件。 容器组件具有ageOrName状态,以及具有“age”和“name”的单选按钮,用于更新此状态。 然后容器组件将此值传递给图形组件。

接收ageOrName作为prop的图形组件使用此prop来更改工具提示中的文本。 但是, 它不起作用 为了绝对完整,我已经包含了2个! 不同的工具提示:

  • 使用d3-tip库创建的工具提示
  • 自定义工具提示,从页面上的div创建

我有两个console.logs()来显示图形组件内的ageOrName prop的值,我已经意识到一些非常令人担忧的事情。 根据我是否在“on.mouseover”调用中,prop的值是不同的 我不知道为什么。

让这个工作对我的应用程序很重要,非常感谢任何帮助!

编辑:几乎肯定会在2天内给出这个问题(现在想)。 即使提供更快的答案,也会奖励。

{buttonName}在选择名称输入后仍然接收年龄的主要原因是在第一次drawGraph调用期间mouseover功能被绑定到圆圈,并且下次再次调用时,会出现d3更新逻辑将新的鼠标悬停事件添加到现有圈子(基于新的{buttonName}

我想出了两种方法来解决这个问题:

  1. 只需获取鼠标悬停功能本身的当前{buttonName} 这是如何做:

     .on('mouseover', function(d) { var { buttonName } = that.props; myD3tip.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`).show(d); 

 class GraphComponent extends React.Component { constructor(props) { super(props); } // Lifecycle Components drawGraph() { var that = this; const { buttonName } = this.props; const myData = [ {x:25, y:30, name:"tommy", age:"24"}, {x:108, y:82, name:"joey", age:"21"}, {x:92, y:107, name:"nicky", age:"23"}, {x:185, y:50, name:"peter", age:"27"}, {x:65, y:80, name:"mickie", age:"4"}, {x:165, y:80, name:"gregie", age:"14"}, {x:154, y:10, name:"tammie", age:"24"}, {x:102, y:42, name:"benny", age:"29"} ] // var myD3tip = d3Tip() var myD3tip = d3.tip() .attr('class', 'd3-tip') .offset([-20,0]) .html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`) const divToolTip = d3.select('div#myTooltip') const points = d3.select('#mySvg').select('g.points') points.call(myD3tip) points .selectAll('circle') .data(myData) .enter() .append('circle') .attr('cx', d => dx) .attr('cy', d => dy) .attr('r', 8) .attr('fill', 'blue'); d3.select('#mySvg').select('g.points').selectAll('circle') .on('mouseover', function(d) { var { buttonName } = that.props; myD3tip.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`).show(d); divToolTip .style('opacity', 1) .style('left', d3.mouse(this)[0] + 'px') .style('top', (d3.mouse(this)[1]) + 'px') .html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`) }) .on('mouseout', function(d) { myD3tip.hide(d) divToolTip .style('opacity', 0) }) } componentDidUpdate() { this.drawGraph() } componentDidMount() { this.drawGraph() } render() { return ( <svg id="mySvg"> <g className="points" /> </svg> ) } } class GraphContainer extends React.Component { constructor(props) { super(props); this.state = { ageOrName: "age" } this.handleChange = this.handleChange.bind(this); } handleChange = (event) => { this.setState({ ageOrName: event.target.value }) } render() { const { ageOrName } = this.state; const ageOrNameOptions = ["age", "name"]; const ageOrNameButtons = <form> <div> {ageOrNameOptions.map((d, i) => { return ( <label key={i+d}> <input type={"radio"} value={ageOrNameOptions[i]} checked={ageOrName === ageOrNameOptions[i]} onChange={this.handleChange} /> <span>{ageOrNameOptions[i]}</span> </label> ) })} </div> </form>; return ( <div> {ageOrNameButtons} <GraphComponent buttonName={ageOrName} /> </div> ) } } ReactDOM.render( <GraphContainer />, document.getElementById('root') ); 
 /* Div ToolTip */ #myTooltip { opacity: 0; position: absolute; pointer-events: none; background-color: lightblue; line-height: 0.50; font-weight: bold; padding: 8px 8px; padding-bottom: 0px; border-radius: 10px; border: 2px solid #444; font-size: 0.9em; } /* ========== */ /* D3 ToolTip */ /* ========== */ .d3-tip { line-height: 0.50; font-weight: bold; padding: 8px 8px; padding-bottom: 0px; border-radius: 10px; border: 2px solid #444; font-size: 0.9em; } /* Creates a small triangle extender for the tooltip */ .d3-tip:after { box-sizing: border-box; display: inline; font-size: 10px; width: 100%; line-height: 1; color: #444; content: "\\25BC"; position: absolute; text-align: center; } /* Style northward tooltips differently */ .d3-tip.n:after { margin: -1px 0 0 0; top: 100%; left: 0; } /* ========== */ 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script> <div id="root"> <svg id="mySvg"></svg> </div> <div id="myTooltip"></div> 

  1. 在每个drawGraph调用期间绑定mouseover事件:(类似这样)

     points.select('circle') .... .attr('fill', 'blue'); d3.select('#mySvg').select('g.points').selectAll('circle') .on('mouseover', function(d) { ..... 

 class GraphComponent extends React.Component { constructor(props) { super(props); } // Lifecycle Components drawGraph() { const { buttonName } = this.props; const myData = [ {x:25, y:30, name:"tommy", age:"24"}, {x:108, y:82, name:"joey", age:"21"}, {x:92, y:107, name:"nicky", age:"23"}, {x:185, y:50, name:"peter", age:"27"}, {x:65, y:80, name:"mickie", age:"4"}, {x:165, y:80, name:"gregie", age:"14"}, {x:154, y:10, name:"tammie", age:"24"}, {x:102, y:42, name:"benny", age:"29"} ] // var myD3tip = d3Tip() var myD3tip = d3.tip() .attr('class', 'd3-tip') .offset([-20,0]) .html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`); const divToolTip = d3.select('div#myTooltip') const points = d3.select('#mySvg').select('g.points') points.call(myD3tip) points .selectAll('circle') .data(myData) .enter() .append('circle') .attr('cx', d => dx) .attr('cy', d => dy) .attr('r', 8) .attr('fill', 'blue'); d3.select('#mySvg').select('g.points').selectAll('circle') .on('mouseover', function(d) { myD3tip.show(d) divToolTip .style('opacity', 1) .style('left', d3.mouse(this)[0] + 'px') .style('top', (d3.mouse(this)[1]) + 'px') .html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`) }) .on('mouseout', function(d) { myD3tip.hide(d) divToolTip .style('opacity', 0) }) } componentDidUpdate() { this.drawGraph() } componentDidMount() { this.drawGraph() } render() { return ( <svg id="mySvg"> <g className="points" /> </svg> ) } } class GraphContainer extends React.Component { constructor(props) { super(props); this.state = { ageOrName: "age" } this.handleChange = this.handleChange.bind(this); } handleChange = (event) => { this.setState({ ageOrName: event.target.value }) } render() { const { ageOrName } = this.state; const ageOrNameOptions = ["age", "name"]; const ageOrNameButtons = <form> <div> {ageOrNameOptions.map((d, i) => { return ( <label key={i+d}> <input type={"radio"} value={ageOrNameOptions[i]} checked={ageOrName === ageOrNameOptions[i]} onChange={this.handleChange} /> <span>{ageOrNameOptions[i]}</span> </label> ) })} </div> </form>; return ( <div> {ageOrNameButtons} <GraphComponent buttonName={ageOrName} /> </div> ) } } ReactDOM.render( <GraphContainer />, document.getElementById('root') ); 
 /* Div ToolTip */ #myTooltip { opacity: 0; position: absolute; pointer-events: none; background-color: lightblue; line-height: 0.50; font-weight: bold; padding: 8px 8px; padding-bottom: 0px; border-radius: 10px; border: 2px solid #444; font-size: 0.9em; } /* ========== */ /* D3 ToolTip */ /* ========== */ .d3-tip { line-height: 0.50; font-weight: bold; padding: 8px 8px; padding-bottom: 0px; border-radius: 10px; border: 2px solid #444; font-size: 0.9em; } /* Creates a small triangle extender for the tooltip */ .d3-tip:after { box-sizing: border-box; display: inline; font-size: 10px; width: 100%; line-height: 1; color: #444; content: "\\25BC"; position: absolute; text-align: center; } /* Style northward tooltips differently */ .d3-tip.n:after { margin: -1px 0 0 0; top: 100%; left: 0; } /* ========== */ 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script> <div id="root"> <svg id="mySvg"></svg> </div> <div id="myTooltip"></div> 

(我个人选择第一种方法)

希望这可以帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM