简体   繁体   English

使用 d3 更新 dom ref 并做出反应

[英]update dom ref using d3 and react

I have a functional component with a d3 function which appends a p to the ref on mount我有一个带有d3函数的functional component ,它在安装时将p附加到ref

Im trying to change the text content of the appended paragraph using the useState hook我正在尝试使用useState挂钩更改附加段落的文本内容

clicking the button <button type="button" onClick={(e)=> setData("bar")}>change data</button> triggers a re-render, instead of changing the text of the p tag , a new version of the d3 function gets created --> appends a new p点击按钮<button type="button" onClick={(e)=> setData("bar")}>change data</button>触发重新渲染,而不是更改p标签的文本,一个新的d3 函数的版本被创建 --> 追加一个新的p

reading How to rerender when refs change suggest to use the useCallback hook which im not quit sure how implement阅读参考更改时如何重新渲染建议使用useCallback钩子,我不确定如何实现

how to achive aa re-render of the paragraph without creating a new one?如何在不创建新段落的情况下实现段落的重新渲染?

import {useEffect, useState, useRef} from 'react'

import * as d3 from 'd3'

function DomReference() {

  const domRef = useRef()

  const [data, setData] = useState("foo")

  useEffect(()=>{


    d3.select(domRef.current)
      .append('p')
      .text(data)
    
    // re-render --> creates a new function 
    
  }, [data])


  return (
    <div ref={domRef}>
      <button type="button" onClick={(e)=> setData("bar")}>change data</button>
      
    </div>
  )
}

export default DomReference

The d3 append function will always create a new element whenever it is executed. d3 append 函数在执行时总是会创建一个新元素。

To have always one paragraph and only update its value, you can use d3.join .要始终只有一个段落并且只更新其值,您可以使用d3.join

instead of:代替:

  d3.select(domRef.current)
      .append('p')
      .text(data)

You could use:你可以使用:

  d3.select(domRef.current).selectAll('p')
    .data([data])
    .join('p')
    .text(d => d)

Explaining line by line:逐行解释:

  • d3.select(domRef.current).selectAll('p') : Selects all the <p> elements in domRef.current d3.select(domRef.current).selectAll('p') : 选择domRef.current所有<p>元素
  • .data([data]) : binds the <p> elements to an array of data. .data([data]) :将<p>元素绑定到数据数组。 When you bind, each item in the array will have its own <p> .绑定时,数组中的每个项目都有自己的<p>
  • .join('p') : The join will create a selection that removes all the <p> that do not have data associated with, and will update the ones that have data. .join('p')join将创建一个选择,删除所有没有关联数据的<p> ,并更新有数据的那些。 This will leave you with one <p> , since the .data() call before only had one element in the array这将为您留下一个<p> ,因为之前的.data()调用在数组中只有一个元素
  • .text(d => d) : This callback is how you access the data that is associated with the <p> . .text(d => d) :此回调是您访问与<p>关联的数据的方式。

As a complement:作为补充:

if you don't want to remove all the other <p> , you can use a class to narrow your selection:如果您不想删除所有其他<p> ,您可以使用一个类来缩小您的选择范围:

  d3.select(domRef.current).selectAll('p.myOddParagraphs')
    .data([1, 3, 5])
    .join('p')
    .classed('myOddParagraphs', true)
    .text(d => d)

   d3.select(domRef.current).selectAll('p.myEvenParagraphs')
    .data([2, 4, 6])
    .join('p')
    .classed('myEvenParagraphs', true)
    .text(d => d)

The code above would result in 6 paragraphs: three are bound to the [1, 3, 5] array, and the other three are bound to [2, 4, 6]上面的代码将导致 6 段:三个绑定到[1, 3, 5]数组,其他三个绑定到[2, 4, 6]

In principle, you shouldn't attempt to mutate DOM using D3 while using React, because React would be unaware of such changes.原则上,你不应该在使用 React 时尝试使用 D3 来改变 DOM,因为 React 不会意识到这样的变化。 This usually leads to very nasty bugs that are difficult to spot and debug.这通常会导致很难发现和调试的非常讨厌的错误。 If you find yourself using attr or append in React, you're using it wrong ;)如果你发现自己在 React 中使用了attrappend ,那你就错了;)

This is not to say you can't use D3 with React though.这并不是说你不能在 React 中使用 D3。 The correct way is to always leave DOM manipulations to React, and use D3 as engine that computes your properties.正确的方法是始终将 DOM 操作留给 React,并使用 D3 作为计算属性的引擎。 Eg when using d3 force layout, you can compute (x,y) coordinates using D3, and pass them as props, so that React handles reconciliation etc.例如,当使用 d3 力布局时,您可以使用 D3 计算 (x,y) 坐标,并将它们作为道具传递,以便 React 处理协调等。

In the above example, I'd suggest leaving the rendering of <p .../> to React.在上面的例子中,我建议将<p .../>的渲染留给 React。 I don't know however what is your end goal so can't really tell what's the correct way, apart from converting this example trivially to React但是,我不知道您的最终目标是什么,因此除了将这个示例简单地转换为 React 之外,无法真正说出正确的方法是什么

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

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