加载后将图像附加到img元素

时间:2018-02-02 19:18:29

标签: javascript reactjs redux

所以我正在制作一个带有项目列表的导航栏,每个项目都有一个图像和一个文本相关联。我使用react-virtualized list,因此基本上我不需要加载所有项目:

  • 每个项目标题都需要从IndexedDB异步加载。这不是一个真正的请求(我使用pouchdb),但它需要几毫秒
  • 需要异步加载每个项子标题,方法是使用PouchDB allDocs然后使用length属性来显示它有多少个孩子(我使用docuri表示ids)
  • 每个项目都有一个要显示的图像,存储为附件
  • 同时显示了十几个项目,因此我无法一次加载所有图像,因为这会在加载时损害性能,所以我需要使用队列,因此一次只加载一个元素。
  • 我不想在这部分中使用redux,因为这些组件在UI中无处不在

我决定去做这样的事情:

export default abstract class LibraryItemComponent extends React.Component {
  protected image: HTMLImageElement;

  abstract loadImage (id: string, img: HTMLImageElement): void;

  abstract loadItem (id: string): void;

  abstract unloadItem (): void;

  componentDidMount() {
    this.loadItem(this.props.id);
    this.loadImage(this.props.id, this.image);
  }

  componentWillReceiveProps (nextProps: LibraryItemComponentProps) {
    if (nextProps.id !== this.props.id) {
      if (nextProps.id) {
        this.loadItem(nextProps.id);
        this.loadImage(nextProps.id, this.image);
      }
    }
  }
  render () 


    return <div >
      <div className="item-image"><img src={BLANK_IMAGE} ref={(ref) => this.image = ref} data-doc-id={this.props.id} /></div>
      <div className="item-props">
        <div className="item-name">
          {this.renderHeader()}
        </div>
        <div className="item-subtitle">
          {this.renderSubtitle()}
        </div>
      </div>
    </div>
  }

loadImage实现如下所示:

  loadImage(id: string, img: HTMLImageElement): void {
    MediaManager.getInstance().load(id, this.image);
  }

来自load的{​​{1}}类似于:

MediaManager

如您所见,加载函数首先加载blob,然后将图像附加到目标。加载并显示图像后,将解析承诺并继续承诺队列,加载下一个请求的图像。

然而,如果图像在仍然加载旧图像时接收到另一个id(因此它现在显示另一个项目标题),则此旧图像仍将(不必要地)加载并显示,仅在新图像加载后消失。

所以我在 load (docId: string, target: HTMLImageElement, watch = true): Promise<Blob> { target.src = BLANK_IMAGE; return this.queue.add(() => { if (!this.shouldAttach(docId, target)) { return Promise.resolve(new Blob()); } return this.database.getAttachment(docId, size).catch(...).then((res: Blob) => { if (!this.shouldAttach(docId, target)) { return Promise.resolve(new Blob()); } return this.attach(res, target, oldSrc); }); }); } attach (blob: Blob, target: HTMLImageElement, oldSrc: string): Promise<Blob> { return new Promise((resolve) => { if (!target || oldSrc !== target.src) { return resolve(blob); } target.src = URL.createObjectURL(blob); target.addEventListener('load', () => { resolve(blob); }); }); } 元素img添加了一个属性,其中包含当前文档ID。然后,由于data-doc-id

,加载和附加功能会自行取消
shouldAttach

问题是,当组件收到新文档时,会调用 shouldAttach (docId: string, target: HTMLImageElement) { if (!document.contains(target)) { // element has been removed from dom return false; } const attr = target.getAttribute('data-doc-id'); if (attr && attr !== docId) { // we already changed document return false; } return true; } ,但如果队列为空,则立即调用ImageManager中的componentWillReceiveProps函数,但load没有尚未更新(因为componentWillReceiveProps)所以它将自行取消。因此,图像无法显示。我该如何解决这个问题?

第一个也很简单的想法就像是

data-doc-id

但是我想尽可能避免直接的DOM突变,因为根据我的经验,它可能会导致React的不稳定。

我正在考虑使用 this.image.setAttribute('data-doc-id', newDocId); 创建一个地图,但实施队列实施起来很难,而且我会失去可重用性

1 个答案:

答案 0 :(得分:1)

我认为最好引用div并在其上添加<img>。这将允许您在其ID更改时删除子项。这也允许您在将图像附加到div之前完全加载图像。 React并不关心,因为div不是React的关注点。