加载新数据时删除 d3-flame-graph

Posted

技术标签:

【中文标题】加载新数据时删除 d3-flame-graph【英文标题】:Removing d3-flame-graph when loading new data 【发布时间】:2020-08-24 16:55:29 【问题描述】:

最近我拿起了一个项目,上面有d3-flame-graph,图表是根据另一个组件上定义的过滤器显示的。 我的问题是,在使用新参数搜索时,我似乎无法清理以前的图表,我想知道是否有人可以帮助我。基本上我现在拥有的是,当我第一次进入页面时,加载组件,然后我有我的图表,当我搜索新日期时,我有加载组件,但最重要的是我仍然有以前的图表

我想我可以在const updateGraph 上使用flamegraph().destroy(),但什么也没发生

import React,  FC, useEffect, useRef, useState, useCallback  from 'react'
import  useParams  from 'react-router-dom'
import moment from 'moment'
import * as d3 from 'd3'
import  flamegraph  from 'd3-flame-graph'

import Filters,  Filter  from '../../../../../../components/Filters'
import  getFlamegraph  from '../../../../../../services/flamegraph'
import  useQueryFilter  from '../../../../../../hooks/filters'
import Flamegraphplaceholder from '../../../../../../components/Placeholders/Flamegraph'

import css from './flamegraph.module.css'

import ToastContainer, 
  useToastContainerMessage,
 from '../../../../../../components/ToastContainer'

const defaultFilters = 
  startDate: moment().subtract(1, 'month'),
  endDate: moment(),
  text: '',
  limit: 10,


const getOffSet = (divElement: htmlDivElement | null) => 
  if (divElement !== null) 
    const padding = 100
    const minGraphHeight = 450

    // ensure that the graph has a min height
    return Math.max(
      window.innerHeight - divElement.offsetTop - padding,
      minGraphHeight
    )
   else 
    const fallBackNavigationHeight = 300

    return window.innerHeight - fallBackNavigationHeight
  


const Flamegraph: FC = () => 
  const [queryFilters, setQueryFilters] = useQueryFilter(defaultFilters)
  const [fetching, setFetching] = useState(false)
  const [graphData, setGraphData] = useState()
  const 
    messages: toastMessages,
    addMessage: addMessageToContainer,
    removeMessage: removeMessageFromContainer,
   = useToastContainerMessage()
  const flameContainerRef = useRef<HTMLDivElement | null>(null)
  const flameRef = useRef<HTMLDivElement | null>(null)
  const graphRef = useRef<any>()
  const graphDataRef = useRef<any>()
  const timerRef = useRef<any>()

  const  projectId, functionId  = useParams()
  let [sourceId, sourceLine] = ['', '']

  if (functionId) 
    ;[sourceId, sourceLine] = functionId.split(':')
  

  const createGraph = () => 
    if (flameContainerRef.current && flameRef.current) 
      graphRef.current = flamegraph()
        .width(flameContainerRef.current.offsetWidth)
        .height(getOffSet(flameRef.current))
        .cellHeight(30)
        .tooltip(false)
        .setColorMapper(function(d, originalColor) 
          // Scale green component proportionally to box width (=> the wider the redder)
          let greenHex = (192 - Math.round((d.x1 - d.x0) * 128)).toString(16)
          return '#FF' + ('0' + greenHex).slice(-2) + '00'
        )
    
  

  const updateGraph = (newData: any) => 
    setGraphData(newData)
    graphDataRef.current = newData

    if (graphRef.current) 
      if (newData === null) 
        graphRef.current.destroy()
        graphRef.current = null
       else 
        d3.select(flameRef.current)
          .datum(newData)
          .call(graphRef.current)
      
    
  

  const fetchGraph = (filters: Filter) => 
    setFetching(true)
    getFlamegraph(
      Number(projectId),
      filters.startDate ? filters.startDate.unix() : 0,
      filters.endDate ? filters.endDate.unix() : 0,
      sourceId,
      sourceLine
    )
      .then(graphData => 
        if (!graphRef.current) 
          createGraph()
        
        updateGraph(graphData)
      )
      .catch(( response ) => 
        updateGraph(null)
        if (response.data) 
          addMessageToContainer(response.data.message, true)
        
      )
      .finally(() => 
        setFetching(false)
      )
  

  const onResize = useCallback(() => 
    clearTimeout(timerRef.current)
    timerRef.current = setTimeout(() => 
      if (graphRef.current && flameContainerRef.current) 
        graphRef.current.width(flameContainerRef.current.offsetWidth)
        d3.select(flameRef.current)
          .datum(graphDataRef.current)
          .call(graphRef.current)
      
    , 500)
  , [])

  useEffect(() => 
    fetchGraph(queryFilters)
    window.addEventListener('resize', onResize)

    return () => 
      window.removeEventListener('resize', onResize)
    
    // eslint-disable-next-line react-hooks/exhaustive-deps
  , [])

  const onChangeFilters = (filters: Filter) => 
    setQueryFilters(filters)
    fetchGraph(filters)
  

  return (
    <div className=css.host>
      <Filters
        defaultValues=queryFilters
        searching=fetching
        onSearch=onChangeFilters
      />
      <div className=css.flameBox>
        <div className=css.flameContainer ref=flameContainerRef>
          <div ref=flameRef />
        </div>
        fetching || !graphData ? (
          <FlamegraphPlaceholder loading=fetching />
        ) : null
      </div>
      <ToastContainer
        messages=toastMessages
        toastDismissed=removeMessageFromContainer
      />
    </div>
  )


export default Flamegraph

【问题讨论】:

【参考方案1】:

首先,flamegraph() 创建flamegraph 的新实例,您需要使用graphref.current.destroy()。其次,您不想在数据已经加载时销毁它,而是在它开始加载时销毁,对吗?因为那是需要时间的操作。

考虑以下几点:

const cleanGraph = () => 
  if (graphref.current !== undefined) 
    graphref.current.destroy()
  


const fetchGraph = (filters: Filter) => 
  setFetching(true)
  cleanGraph()
  getFlamegraph(
    Number(projectId),
    filters.startDate ? filters.startDate.unix() : 0,
    filters.endDate ? filters.endDate.unix() : 0,
    sourceId,
    sourceLine
  )
  ...

【讨论】:

以上是关于加载新数据时删除 d3-flame-graph的主要内容,如果未能解决你的问题,请参考以下文章

仅使用 ajax 加载新数据

内联更新后,Kendo Treelist重新加载/刷新

滚动时UITableView不加载单元格数据

删除对象后重新加载 UICollectionViewController?

TableView 未使用来自 AFNetworking 响应对象的新数据重新加载

加载新数据时RecyclerView滚动顶部