You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React中正确实现IGV.js?解决容器重复问题

在React中正确实现IGV.js并解决容器重复问题

问题背景

在原生JS中使用IGV.js时发现会生成两个容器div:

var igvDiv = document.getElementById("igv-div");
var options =
{
    genome: "hg38",
    locus: "chr8:127,736,588-127,739,371",
    tracks: [
        {
            "name": "HG00103",
            "url": "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram",
            "indexURL": "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram.crai",
            "format": "cram"
        }
    ]
};

igv.createBrowser(igvDiv, options)
        .then(function (browser) {
            console.log("Created IGV browser");
        })

随后尝试用React类组件实现(引入了useRef但未实际使用):

import React, { useRef, useEffect, Component } from 'react';

import igv from 'igv';

var igvStyle = {
  fontFamily: 'open sans,helveticaneue,helvetica neue,Helvetica,Arial,sans-serif',
    paddingTop: '60px',
    margin: '5px'
  }
class IGViewerSection extends Component {

  componentDidMount() {
    var igvContainer = document.getElementById('igv-div');
    var igvOptions = {
      genome: "hg38",
      locus: "chr8:127,736,588-127,739,371",
      tracks: [
          {
              "name": "HG00103",
              "url": "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram",
              "indexURL": "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram.crai",
              "format": "cram"
          }
      ]
  };
    return igv.createBrowser(igvContainer, igvOptions);
  }

  render() {
    return (
      <div id="igv-div" style={igvStyle}></div>
    );
  }
}

export default IGViewerSection;

需要解决两个核心问题:容器重复生成的原因,以及React中正确集成IGV.js的方式。

容器重复的原因

IGV.js的createBrowser方法会在传入的容器内部自动创建一个新div作为浏览器的根容器,并非直接在传入的容器上渲染。你看到的两个div是:

  • 自定义的外层容器<div id="igv-div">
  • IGV.js内部生成的实际浏览器容器

这是IGV.js的默认行为,并非bug,但在React中需避免重复初始化或DOM冲突。

React中正确实现IGV.js的方法

1. 类组件优化方案(修复问题并规范使用Ref)

放弃document.getElementById,改用React的createRef获取容器,同时在组件卸载时销毁IGV实例,防止内存泄漏:

import React, { Component, createRef } from 'react';
import igv from 'igv';

const igvStyle = {
  fontFamily: 'open sans,helveticaneue,helvetica neue,Helvetica,Arial,sans-serif',
  paddingTop: '60px',
  margin: '5px'
};

class IGViewerSection extends Component {
  // 创建Ref引用容器
  igvContainerRef = createRef();
  // 存储IGV浏览器实例
  igvBrowser = null;

  componentDidMount() {
    const igvOptions = {
      genome: "hg38",
      locus: "chr8:127,736,588-127,739,371",
      tracks: [
        {
          name: "HG00103",
          url: "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram",
          indexURL: "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram.crai",
          format: "cram"
        }
      ]
    };

    // 通过Ref获取DOM节点
    igv.createBrowser(this.igvContainerRef.current, igvOptions)
      .then(browser => {
        this.igvBrowser = browser;
        console.log("IGV浏览器创建完成");
      });
  }

  componentWillUnmount() {
    // 组件卸载时销毁IGV实例,清理资源
    if (this.igvBrowser) {
      this.igvBrowser.destroy();
    }
  }

  render() {
    return (
      <div ref={this.igvContainerRef} style={igvStyle}></div>
    );
  }
}

export default IGViewerSection;

2. 函数组件方案(使用useRef和useEffect)

如果偏好函数组件,用useRef获取容器,useEffect处理初始化和清理逻辑:

import React, { useRef, useEffect } from 'react';
import igv from 'igv';

const igvStyle = {
  fontFamily: 'open sans,helveticaneue,helvetica neue,Helvetica,Arial,sans-serif',
  paddingTop: '60px',
  margin: '5px'
};

const IGViewerSection = () => {
  const igvContainerRef = useRef(null);
  const igvBrowserRef = useRef(null);

  useEffect(() => {
    if (!igvContainerRef.current || igvBrowserRef.current) return;

    const igvOptions = {
      genome: "hg38",
      locus: "chr8:127,736,588-127,739,371",
      tracks: [
        {
          name: "HG00103",
          url: "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram",
          indexURL: "https://s3.amazonaws.com/1000genomes/data/HG00103/alignment/HG00103.alt_bwamem_GRCh38DH.20150718.GBR.low_coverage.cram.crai",
          format: "cram"
        }
      ]
    };

    igv.createBrowser(igvContainerRef.current, igvOptions)
      .then(browser => {
        igvBrowserRef.current = browser;
        console.log("IGV浏览器创建完成");
      });

    // 清理函数,组件卸载时销毁实例
    return () => {
      if (igvBrowserRef.current) {
        igvBrowserRef.current.destroy();
        igvBrowserRef.current = null;
      }
    };
  }, []);

  return (
    <div ref={igvContainerRef} style={igvStyle}></div>
  );
};

export default IGViewerSection;

关键注意事项

  • 避免重复初始化:确保createBrowser只执行一次,类组件放在componentDidMount,函数组件用useEffect空依赖数组。
  • 使用React Ref而非直接操作DOM:getElementById容易引发React DOM diff冲突,Ref是官方推荐的DOM节点获取方式。
  • 销毁实例清理资源:组件卸载时调用browser.destroy(),防止内存泄漏和残留DOM节点。
  • 容器重复的说明:IGV.js内部生成的div是正常行为,只要确保只初始化一次,就不会出现多余容器。若仍有重复,检查是否多次调用了createBrowser。

内容的提问来源于stack exchange,提问作者Victor Banerjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 17:01:13