React-Virtualized table 增长到最大高度 [英] React-Virtualized table to grow up to a maximal height

查看:72
本文介绍了React-Virtualized table 增长到最大高度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题:我想要一个 React Virtualized table 可以占据最大的高度空间(比如 500px).在表格的最后可见行下方,我想添加另一个元素.有点像这样:

如果表格内容小于500px+-----------------------------------------+ ----+|标题1 |标题2 |... ||+---------+---------+---------------------+ |小于 500 像素,|第 1 行 |第 1 行 |第 1 行 ||说 200 像素+---------+---------+---------------------+ ||第 2 行 |第 2 行 |第 2 行 ||+---------+---------+---------------------+ ----+其他内容如果表格内容超过500px+-----------------------------------------+^ ----+|标题1 |标题2 |... |||+---------+---------+---------------------+||500 像素,|第 1 行 |第 1 行 |第 1 行 |||注意+---------+---------+---------------------+||滚动条|第 2 行 |第 2 行 |第 2 行 |||+---------+---------+---------------------+|||第 3 行 |第 3 行 |第 3 行 |||+---------+---------+---------------------+|||第 4 行 |第 4 行 |第 4 行 |V ----+其他内容

换句话说:表格和其他内容"之间不应有白色间隙,而且表格最多应占用 500 像素.

问题:我怎样才能做到这一点?

我的尝试:我尝试过 AutoSizer,但问题是:

  • 如果 AutoSizer 包含在一个只有固定高度的 div 中(例如:height: 500px),那么有如果表格太短,表格和其他内容"之间将出现白色间隙;所以我得到的是:

    +-----------------------------------------+ ----+|标题1 |标题2 |... ||+---------+---------+---------------------+ |小于 500 像素,|第 1 行 |第 1 行 |第 1 行 ||说 200 像素+---------+---------+---------------------+ ||第 2 行 |第 2 行 |第 2 行 ||+---------+---------+---------------------+ ----+||差距|300像素||其他内容----+

  • 如果 AutoSizer 包含在只有最大高度的 div 中(例如:max-height: 500px),然后只显示标题,其他内容"将在标题上方(即包含的 div 不会随着表格的增长而增长,因此 AutoSizer 会没有);我得到的是这个:

    其他内容----------------------------+|标题1 |标题2 |... |+---------+---------+---------------------+

  • (如果我同时给出最大高度和固定高度,那么当然固定高度获胜",结果与第一种情况相同.)

备注:我完全不相信 AutoSizer 是这里的正确方法,所以我愿意接受任何其他建议(如果可能,没有进一步第三方依赖,但也可以,如果没有其他办法).

参考来源:

 

<div style={{height: "100px", maxHeight: "500px"}}><AutoSizer>{({高度,宽度}) =>(<表参考 =表"disableHeader={false}headerClassName=""标头高度={40}高度={高度}行类名=""行高度={40}rowGetter={rowGetter}行数={6}宽度={宽度}><列标签=索引"cellDataGetter={({rowData}) =>rowData.index}数据键=索引"宽度={60}/><列标签=文本1"cellDataGetter={({rowData}) =>rowData.text1}数据键=文本1"宽度={90}/><列标签=文本2"cellDataGetter={({rowData}) =>rowData.text2}数据键=文本2"宽度={90}/></表>)}</AutoSizer>

<div>其他一些文字</div>

<小时>

更新

约束的澄清:解决方案必须使用 Virtualized-React 表.不能选择使用 HTML 表格.(上面我只是说 AutoSizer 可能不是正确的解决方案,但我确实需要反应表.)

答案中的建议方法:我尝试将@jered 的建议应用于我的案例,再次得到一个延伸到最大高度的桌子.我不确定我是否做错了什么,或者建议的方法是否在这种情况下根本不起作用

 

<div 样式={{显示:'弹性',最大高度:'500px',边框:'1px纯蓝色',填充:'2px',边距:'2px',溢出:'滚动'}}><表参考 =表"disableHeader={false}headerClassName=""标头高度={40}高度={900}行类名=""行高度={40}rowGetter={rowGetter}行数={6}宽度={400}><列标签=索引"cellDataGetter={({rowData}) =>rowData.index}数据键=索引"宽度={60}/><列标签=文本1"cellDataGetter={({rowData}) =>rowData.text1}数据键=文本1"宽度={90}/><列标签=文本2"cellDataGetter={({rowData}) =>rowData.text2}数据键=文本2"宽度={90}/></表>

<div style={{ display: 'flex',flexDirection: '列',边框:'1px纯绿色',填充:'2px',margin: '2px' }}>其他一些文字</div>

相关问题:基本上,React Virtualized table 创建了一个 div ,其大小为 0px x 0px 并且其内容可见溢出.因此,如果可以考虑溢出的大小,这将解决问题.这本身就是一个有趣的问题,所以我会打开一个跟进,但保持这个开放,因为对于这种特定情况可能有更好的解决方案.

另外一个更新:我注意到如果我把表格放在一个 flex-container 中,零大小的 div 没有被创建.

解决方案

我能够使用 rowRenderer 和 refs 为 React 虚拟化 List 实现这一点,但我相信它也应该适用于 Table.

  1. 创建包含列表高度的状态,将默认值设为您预期的最大高度:

const [listHeight, setListHeight] = useState(500);

  1. 渲染列表时,使用此状态的高度:

  1. 为列表中的每个项目创建一个引用列表(在本例中,元素是 div):

const rowRefs = useRef(dataInList.map(() => React.createRef()));

  1. 在 rowRenderer 上,将引用附加到代表表中行的 DOM 元素(我也在使用 cellMeasurer)

<代码>const rowRenderer = useCallback(({key, index, style, parent }: ListRowProps) =>{const item = dataInList[index];返回 (<CellMeasurer键={键}缓存={缓存.current}列索引={0}父母={父母}rowIndex={index}><div ref={rowRefs.current[index]}>你的行信息在这里

</CellMeasurer>);},[格式化]);

  1. 创建一个在数据源更改时触发的效果(或者可能只在组件挂载时触发一次).该效果将使用 refs offsetHeight 对行的高度求和,并将状态 listHeight 设置为该高度或您想要的任何最大高度(以两者中的较小者为准).

useEffect(() => {const domListHeight = rowRefs.current.filter((e: any) => e.current).reduce((acc: any, e: any) => acc + e.current.offsetHeight, 0);const MAX_HEIGHT = 500;const usableHeight = domListHeight <最大高度 ?domListHeight : MAX_HEIGHT;设置列表高度(可用高度);}, [dataInList]);

总结:组件将在最大高度渲染列表,然后计算所有行组合的实际高度,如果总高度小于预期的最大高度,组件将从头开始重新渲染列表新的低高度.

Problem: I would like to have a React Virtualized table that can take up a maximal space in height (let's say 500px). Immediately below the last visible row of the table, I would like to add another element. Somehow like this:

If the content of the table is shorter than 500px

+-----------------------------------------+    ----+
| Header1 | Header2 | ...                 |        |
+---------+---------+---------------------+        |  less than 500px,
| row1    | row1    | row1                |        |  say 200px
+---------+---------+---------------------+        |
| row2    | row2    | row2                |        |
+---------+---------+---------------------+    ----+
Other content


If the content of the table is longer than 500px

+-----------------------------------------+^   ----+
| Header1 | Header2 | ...                 ||       |
+---------+---------+---------------------+|       |  500px,
| row1    | row1    | row1                ||       |  notice the
+---------+---------+---------------------+|       |  scroll bar
| row2    | row2    | row2                ||       |
+---------+---------+---------------------+|       |
| row3    | row3    | row3                ||       |
+---------+---------+---------------------+|       |
| row4    | row4    | row4                |V   ----+
Other content

In other words: there should be no white gap between the table and "Other content", but also the table should take up a maximum of 500px.

Question: How can I achieve this?

My attempt: I've tried with AutoSizer, but the issue is:

  • if the AutoSizer is contained in a div with only a fixed height (e.g.: height: 500px), then there will be a white gap between the table and "Other content" if the table is too short; so what I get is this:

    +-----------------------------------------+    ----+
    | Header1 | Header2 | ...                 |        |
    +---------+---------+---------------------+        |  less than 500px,
    | row1    | row1    | row1                |        |  say 200px
    +---------+---------+---------------------+        |
    | row2    | row2    | row2                |        |
    +---------+---------+---------------------+    ----+
                                                       |
                                                       | gap of        
                                                       | 300px
                                                       |        
                                                       |
    Other content                                  ----+
    

  • if the AutoSizer is contained in a div with only a maximal height (e.g.: max-height: 500px), then only the header is shown, and the "Other content" will be above the header (i.e. the containing div does not grow as the table would grow, and thus AutoSizer does nothing); what I get is this:

    Other content ----------------------------+
    | Header1 | Header2 | ...                 |
    +---------+---------+---------------------+
    

  • (If I give both maximal and fixed height, then of course the fixed height "wins", and the result is the same as in the first case.)

Remark: I'm not convinced at all, that AutoSizer is the correct approach here, so I'm open to any other suggestion (if possible, without further third-party dependencies, but that is ok too, if there is no other way).

Source for reference:

 <div>
   <div style={{height: "100px", maxHeight: "500px"}}>
      <AutoSizer>
        {({height, width}) => (
          <Table
            ref="Table"
            disableHeader={false}
            headerClassName=""
            headerHeight={40}
            height={height}
            rowClassName=""
            rowHeight={40}
            rowGetter={rowGetter}
            rowCount={6}
            width={width}>
            <Column
                label="Index"
                cellDataGetter={({rowData}) => rowData.index}
                dataKey="index"
                width={60}
            />
            <Column
              label="Text1"
              cellDataGetter={({rowData}) => rowData.text1}
              dataKey="text1"
              width={90}
            />
            <Column
              label="Text2"
              cellDataGetter={({rowData}) => rowData.text2}
              dataKey="text2"
              width={90}
            />
          </Table>
        )}
      </AutoSizer>             
  </div>
  <div>Some other text</div>
 </div>


Updates

Clarification of constraints: The solution has to work with Virtualized-React table. It is not an option to use HTML table. (Above I only said that maybe AutoSizer is not the right solution, but I do need react table.)

Suggested approach in the answer: I tried to apply @jered's suggestion to my case, and again, got a table which extends to the full max-height. I'm not sure if I did something wrong, or if the suggested approach does not work at all for this situation

   <div style={{  display: 'flex',
                  border: '1px solid red',
                  flexDirection: 'column' }}>
    <div style={{
                  display: 'flex',
                  maxHeight: '500px',
                  border: '1px solid blue',
                  padding: '2px',
                  margin: '2px',
                  overflow: 'scroll'
          }}>
          <Table
            ref="Table"
            disableHeader={false}
            headerClassName=""
            headerHeight={40}
            height={900}
            rowClassName=""
            rowHeight={40}
            rowGetter={rowGetter}
            rowCount={6}
            width={400}>
            <Column
                label="Index"
                cellDataGetter={({rowData}) => rowData.index}
                dataKey="index"
                width={60}
            />
            <Column
              label="Text1"
              cellDataGetter={({rowData}) => rowData.text1}
              dataKey="text1"
              width={90}
            />
            <Column
              label="Text2"
              cellDataGetter={({rowData}) => rowData.text2}
              dataKey="text2"
              width={90}
            />
          </Table>
   </div>
  <div style={{   display: 'flex',
                  flexDirection: 'column',
                  border: '1px solid green',
                  padding: '2px',
                  margin: '2px' }}>Some other text</div>
 </div>

Related question: Basically React Virtualized table creates a div with 0px x 0px size and visible overflow of its content. Thus, if the size of the overflow can be considered, this would solve the issue. This is an interesting question in itself, so I'll open a follow-up, but leave this one open, as there might be a better solution for this specific case.

One more update: I noticed that the zero-size div is not created, if I put the table inside a flex-container.

解决方案

I was able to accomplish this for React virtualized List using rowRenderer and refs, but I believe it should work with Table as well.

  1. Create state to contain the height of the list, make the default your intended max height:

const [listHeight, setListHeight] = useState(500);

  1. When rendering the List, use the height from this state:

<List height={listHeight} .../>

  1. Create a list of refs for each item in your list (in this case the elements are divs):

const rowRefs = useRef(dataInList.map(() => React.createRef<HTMLDivElement>()));

  1. On the rowRenderer, attach the refs to the DOM elements that are representing the rows in your table (I'm using cellMeasurer as well)


const rowRenderer = useCallback(
  ({ key, index, style, parent }: ListRowProps) => {
    const item = dataInList[index];
    return (
      <CellMeasurer
        key={key}
        cache={cache.current}
        columnIndex={0}
        parent={parent}
        rowIndex={index}>
          <div ref={rowRefs.current[index]}>
              Your row info goes here 
          </div>
      </CellMeasurer>
    );
  },
  [formatted]
);

  1. Create an effect to trigger when the data source changes (or possibly just once when the component mounts). The effect will sum the heights of the rows using the refs offsetHeight and set the state listHeight to that or whatever max height you want (whichever is less of the two).

useEffect(() => {
    const domListHeight = rowRefs.current
        .filter((e: any) => e.current)
        .reduce((acc: any, e: any) => acc + e.current.offsetHeight, 0);
    
    const MAX_HEIGHT = 500;
    const usableHeight = domListHeight < MAX_HEIGHT ? domListHeight : MAX_HEIGHT;
    
    setListHeight(usableHeight);
}, [dataInList]);

Summary: The component will render the list at the max height and then calculate the actual height of all the rows combined, if that total is less than the max height intended, the component will re-render the list from scratch with that new lower height.

这篇关于React-Virtualized table 增长到最大高度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆