reactjs 当渲染未被触发时,在虚拟化列表中进行短暂高亮显示

rbpvctlc  于 2023-01-25  发布在  React
关注(0)|答案(1)|浏览(101)

我在一个应用程序中有一个很大的项目列表,所以它是使用react-virtuoso提供的虚拟列表呈现的。列表本身的内容根据一个单独组件所做的API调用而改变。我试图实现的是,每当一个新项目添加到列表中时,列表会自动滚动到该项目,然后将其高亮显示一秒钟。
我设法想出的办法是让另一个组件将新创建项的id放在虚拟列表可以访问的上下文中。

function MyList(props) {

  const { collection } = props;
  
  const { getLastId } useApiResultsContext();
  
  cosnt highlightIndex = useRef();
  const listRef = useRef(null);
  
  const turnHighlightOff = useCallback(() => {
    highlighIndex.current = undefined;
  }, []);
  
  useEffect(() => {
    const id = getLastId(); 
    // calling this function also resets the lastId inside the context,
    // so next time it is called it will return undefined
    // unless another item was entered
    
    if (!id) return;
    
    const index = collection.findIndex((item) => item.id === if);
    
    if (index < 0) return;
    
    listRef.current?.scrollToIndex({ index, align: 'start' });
    
    highlightIndex.current = index;
  }, [collection, getLastId]);
  
  return (
    <Virtuoso
      ref={listRef}
      data={collection}
      itemContent={(index, item) => (
        <ItemRow
          content={item}
          toHighlight={highlighIndex.current}
          checkHighlight={turnHighlightOff}
        />
      )}
    />
  ); 
}

我在这里使用useRef而不是useState,因为使用状态会破坏整个过程-我猜是因为Virtuouso在滚动时实际上不会重新渲染。使用useRef,一切都运行良好。在ItemRow中,高亮显示是这样管理的:

function ItemRow(props) {
 const { content, toHighlight, checkHighligh } = props;
 
 const highlightMe = toHighlight;
 
 useEffect(() => {
  toHighlight && checkHighlight && checkHighligh();
 });
 
 return (
  <div className={highlightMe ? 'highligh' : undefined}>
    // ... The rest of the render
  </div>
 );
}

在CSS中,我为highligh类定义了一个1秒的动画,其中background-color发生了变化。
到目前为止,一切都完全按照我的想法运行,除了一个我不知道如何解决的问题:如果列表滚动到帧外的一行,高亮显示会很好地工作,因为该行会被渲染。但是,如果该行已经在帧内,react-virtuoso不需要渲染它,因此,因为我使用的是ref而不是state,高亮显示永远不会被调用。正如我上面提到的,使用useState破坏了整个过程,所以我最终使用useRef。但我不知道如何在所需行已经在视图中时强制重新呈现它。

7cwmlq89

7cwmlq891#

我“有点”解决了这个问题。我的解决方案不是最好的,在一些罕见的情况下,没有突出显示行,因为我想要的,但这是我能想出的最好的,除非有人在这里有更好的主意。
解决方案的核心是改变上下文公开的getLastId背后的思想。以前,只要useEffect中的组件绘制了id,它就会将id重置回undefined。现在,上下文公开了两个函数-一个函数用于获取id,另一个函数用于重置id。基本上,getLastIdresetLastId在幕后操纵一个ref对象,而不是一个状态,以防止不必要的渲染。现在,MyList组件看起来像这样:

function MyList(props) {

  const { collection } = props;
  
  const { getLastId, resetLastId } useApiResultsContext();
  
  cosnt highlightIndex = useRef();
  const listRef = useRef(null);
  
  const turnHighlightOff = useCallback(() => {
    highlighIndex.current = undefined;
  }, []);
  
  useEffect(() => {
    const id = getLastId();
    resetLastId();
    
    if (!id) return;
    
    const index = collection.findIndex((item) => item.id === if);
    
    if (index < 0) return;
    
    listRef.current?.scrollToIndex({ index, align: 'start' });
    
    highlightIndex.current = index;
  }, [collection, getLastId]);
  
  return (
    <Virtuoso
      ref={listRef}
      data={collection}
      itemContent={(index, item) => (
        <ItemRow
          content={item}
          toHighlight={highlighIndex.current === index || getLastId() === item.id}
          checkHighlight={turnHighlightOff}
        />
      )}
    />
  ); 
}

现在,在useEffect中设置highlightIndex处理视口之外的项,而将getLastId调用提供给每个ItemRow的属性处理视图中已经存在的项。

相关问题