Using react-virtualized to implement a long list of images with dynamic height

Using react-virtualized to implement a long list of images with dynamic height

Virtual list is a technology that renders a portion of the data in a long list according to the visible area of ​​the scroll container element. Virtual lists are a common optimization for long list scenarios. After all, few people render hundreds of sub-elements in a list. It is only necessary to render the elements in the visible area when the scroll bar scrolls horizontally or vertically.

Problems encountered during development

1. The pictures in the long list should maintain the same proportion as the original pictures. When the width remains unchanged during vertical scrolling, the height of each picture is dynamic. When the height of the list item changes, it will affect the position information of the list item and all subsequent list items.

2. The image width and height can only be obtained after the image is loaded.

Solution

We use the list component in react-virtualized, the official example

import React from 'react';
import ReactDOM from 'react-dom';
import {List} from 'react-virtualized';

// List data as an array of strings
const list = [
  'Brian Vaughn',
  // And so on...
];

function rowRenderer({
  key, // Unique key within array of rows
  index, // Index of row within collection
  isScrolling, // The List is currently being scrolled
  isVisible, // This row is visible within the List (eg it is not an overscanned row)
  style, // Style object to be applied to row (to position it)
}) {
  return (
    <div key={key} style={style}>
      {list[index]}
    </div>
  );
}

// Render your list
ReactDOM.render(
  <List
    width={300}
    height={300}
    rowCount={list.length}
    rowHeight={20}
    rowRenderer={rowRenderer}
  />,
  document.getElementById('example'),
); 

Where rowHeight is the height of each row. You can pass in a fixed height or a function. Each time the height of a child element changes, the recomputeRowHeights method needs to be called to recalculate the row height and offset after specifying the index.

Specific implementation

const ImgHeightComponent = ({ imgUrl, onHeightReady, height, width }) => {
  const [style, setStyle] = useState({
    height,
    width,
    display: 'block',
  })
  const getImgWithAndHeight = (url) => {
    return new Promise((resolve, reject) => {
      var img = new Image()
      // Change the src of the image
      img.src = url
      let set = null
      const onload = () => {
        if (img.width || img.height) {
          //Image loading completed clearInterval(set)
          resolve({ width: img.width, height: img.height })
        }
      }
      set = setInterval(onload, 40)
    })
  }

  useEffect(() => {
    getImgWithAndHeight(imgUrl).then((size) => {
      const currentHeight = size.height * (width / size.width)
      setStyle({
        height: currentHeight,
        width: width,
        display: 'block',
      })
      onHeightReady(currentHeight)
    })
  }, [])
  return <img src={imgUrl} alt='' style={style} />
}

First, write a component to obtain the image height, obtain and calculate the height through a timed loop detection and pass it to the parent component.

import React, { useState, useEffect, useRef } from 'react'
import styles from './index.scss'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List } from 'react-virtualized/dist/commonjs/List'

export default class DocumentStudy extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [], 
      heights: [],
      autoWidth:900,
      autoHeight: 300
    }
  }

  handleHeightReady = (height, index) => {
    this.setState(
      (state) => {
        const flag = state.heights.some((item) => item.index === index)
        if (!flag) {
          return {
            heights: [
              ...state.heights,
              {
                index,
                height,
              },
            ],
          }
        }
        return {
          heights: state.heights,
        }
      },
      () => {
        this.listRef.recomputeRowHeights(index)
      },
    )
  }

  getRowHeight = ({ index }) => {
    const row = this.state.heights.find((item) => item.index === index)
    return row ? row.height : this.state.autoHeight
  }

  renderItem = ({ index, key, style }) => {
    const { list, autoWidth, autoHeight } = this.state
    if (this.state.heights.find((item) => item.index === index)) {
      return (
        <div key={key} style={style}>
          <img src={list[index].imgUrl} alt='' style={{width: '100%'}}/>
        </div>
      )
    }

    return (
      <div key={key} style={style}>
        <ImgHeightComponent
          imgUrl={list[index].imgUrl}
          width={autoWidth}
          height={autoHeight}
          onHeightReady={(height) => {
            this.handleHeightReady(height, index)
          }}
        />
      </div>
    )
  }

  render() {
    const { list } = this.state
    return (
      <>
        <div style={{ height: 1000 }}>
          <AutoSizer>
            {({ width, height }) => (
              <List
                ref={(ref) => (this.listRef = ref)}
                width={width}
                height={height}
                overscanRowCount={10}
                rowCount={list.length}
                rowRenderer={this.renderItem}
                rowHeight={this.getRowHeight}
              />
            )}
          </AutoSizer>
        </div>
      </>
    )
  }
}

The parent component collects the heights of all images through the handleHeightReady method, and calls the recomputeRowHeights method of the List component every time the height changes to notify the component to recalculate the height and offset. At this point, the problems encountered have basically been solved.

Actual Results

summary

Currently, we only use react-virtualized to implement the long list of images. The specific internal implementation of react-virtualized needs further study.

The above is the details of using react-virtualized to implement a long list of dynamic images. For more information about react virtualized long lists, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Example analysis of the usage of ScrollView component carousel and ListView rendering list component in React Native
  • React mobile terminal implements the sample code of swiping left to delete the list
  • React Native custom pull-down refresh pull-up loaded list example
  • React Native indexed city list component example code
  • React implements clicking to delete the corresponding item in the list

<<:  Tutorial on installing php5, uninstalling php, and installing php7 on centos

>>:  How to create a new user in CentOS and enable key login

Recommend

Implementation of form submission in html

Form submission code 1. Source code analysis <...

A brief discussion on mobile terminal adaptation

Preface The writing of front-end code can never e...

Linux CentOS6.9 installation graphic tutorial under VMware

As a technical novice, I am recording the process...

HTML scroll bar textarea attribute setting

1. Overflow content overflow settings (set whether...

HTML table tag tutorial (45): table body tag

The <tbody> tag is used to define the style...

About the problems of congruence and inequality, equality and inequality in JS

Table of contents Congruent and Incongruent congr...

Introduction to useRef and useState in JavaScript

Table of contents 1. useState hook 2. useRef hook...

Summary of Vue component basics

Component Basics 1 Component Reuse Components are...

The vue project realizes drawing a watermark in a certain area

This article shares with you how to use Vue to dr...

How to quickly install tensorflow environment in Docker

Quickly install the tensorflow environment in Doc...

What are the image file formats and how to choose

1. Which three formats? They are: gif, jpg, and pn...

Linux sar command usage and code example analysis

1. CPU utilization sar -p (view all day) sar -u 1...