【问题标题】:React-Virtualized table to grow up to a maximal heightReact-Virtualized table 增长到最大高度
【发布时间】:2019-03-18 01:08:19
【问题描述】:

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

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

也就是说:表格和“其他内容”之间不应该有空白,而且表格应该最多占用500px。

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

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

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

    +-----------------------------------------+    ----+
    | Header1 | Header2 | ...                 |        |
    +---------+---------+---------------------+        |  less than 500px,
    | row1    | row1    | row1                |        |  say 200px
    +---------+---------+---------------------+        |
    | row2    | row2    | row2                |        |
    +---------+---------+---------------------+    ----+
                                                       |
                                                       | gap of        
                                                       | 300px
                                                       |        
                                                       |
    Other content                                  ----+
    
  • 如果AutoSizer 包含在仅具有最大高度的div 中(例如:max-height: 500px),则仅显示标题,并且“其他内容”将位于标题上方(即包含div 不会随着表的增长而增长,因此 AutoSizer 什么也不做);我得到的是这样的:

    Other content ----------------------------+
    | Header1 | Header2 | ...                 |
    +---------+---------+---------------------+
    
  • (如果我同时给出最大高度和固定高度,那么当然是固定高度“获胜”,结果与第一种情况相同。)

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

参考来源:

 <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>

更新

约束说明:解决方案必须使用 Virtualized-React 表。使用 HTML 表格不是一个选项。 (上面我只说AutoSizer可能不是正确的解决方案,但我确实需要react table。)

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

   <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>

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

另一个更新:我注意到如果我将表格放在 flex-container 中,则不会创建零大小的 div。

【问题讨论】:

    标签: html css reactjs


    【解决方案1】:

    无需求助于 React 或任何复杂的东西。只需在简单的 CSS 中使用 FlexBox。

    提供“表格”和“其他内容”的容器display: flex,并在包裹&lt;table&gt; 的内部元素上设置max-height。 FlexBox 子级将尝试扩展以填充其内容所需的空间,但不会扩展超过其max-height(如果已设置)。

    注意:展开 sn-p 到全屏可以看到完整的效果。

    function adjustrows() {
      let val = document.getElementById("slider").value;
      let table = document.getElementById("table");
      let inner = `
        <tr>
          <th>foo</th>
          <th>bar</th>
          <th>baz</th>
          <th>foo</th>
          <th>bar</th>
          <th>baz</th>
        </tr>
      `;
      for (let i = 0; i < val; i++) {
        inner += `
          <tr>
            <td>foo</td>
            <td>bar</td>
            <td>baz</td>
            <td>foo</td>
            <td>bar</td>
            <td>baz</td>
          </tr>
        `;
      }
      table.innerHTML = inner;
      document.getElementById("numrows").innerText = val;
    }
    adjustrows();
    table, tr, th, td {
      box-sizing: border-box;
      border: 1px solid gray;
    }
    
    table {
      flex-grow: 1;
    }
    
    th {
      background: lightgray;
    }
    
    label {
      display: flex;
      justify-content: center;
      margin: 5px;
    }
    
    #numrows {
      margin: 0 10px;
    }
    
    #flex-container {
      display: flex;
      border: 1px solid red;
      flex-direction: column;
    }
    
    #table-container {
      display: flex;
      max-height: 500px;
      border: 1px solid blue;
      padding: 2px;
      margin: 2px;
      overflow: scroll;
    }
    
    #other-content-container {
      display: flex;
      flex-direction: column;
      border: 1px solid green;
      padding: 2px;
      margin: 2px;
    }
    
    .placeholder {
      background: darkgray;
      margin: 10px;
      height: 120px;
    }
    <div id="flex-container">
      <label>
        <strong>Number of table rows:<span id="numrows"></span> </strong>
        <input id="slider" onchange="adjustrows();" oninput="adjustrows();" type="range" min="0" max="100" value="1" />
      </label>
      <div id="table-container">
        <table id="table">
        </table>
      </div>
      <div id="other-content-container">
        <div class="placeholder"></div>
        <div class="placeholder"></div>
        <div class="placeholder"></div>
        <div class="placeholder"></div>
        <div class="placeholder"></div>
      </div>
    </div>

    请注意,如果您从表中删除一些&lt;tr&gt; 元素,使其比500px 短,#table-container 将简单地缩小以适应。

    与往常一样,Mozilla 开发者网络在 FlexBox 上有 some pretty good info

    【讨论】:

    • 谢谢@jered:我尝试应用您的建议,但桌子再次伸展以占据完整的最大高度(请参阅更新的答案)。问题是,我确实需要 React Table,因为它有额外的功能(此时,不能从头开始实现类似的东西)。
    • @Attilio 可以使用 React Table。应该可以应用您自己的 CSS 样式来让我的 flexbox 解决方案发挥作用。您可以发布一些您尝试过的代码或一个实时示例吗?
    【解决方案2】:

    我能够使用 rowRenderer 和 refs 为 React 虚拟化 List 完成此操作,但我相信它也应该与 Table 一起使用。

    1. 创建状态以包含列表的高度,将默认设置设为您想要的最大高度:

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

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

    &lt;List height={listHeight} .../&gt;

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

    const rowRefs = useRef(dataInList.map(() =&gt; React.createRef&lt;HTMLDivElement&gt;()));

    1. 在 rowRenderer 上,将引用附加到表示表中行的 DOM 元素(我也在使用 cellMeasurer)
    
    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. 创建在数据源更改时触发的效果(或者可能仅在组件安装时触发一次)。该效果将使用引用 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 < MAX_HEIGHT ? domListHeight : MAX_HEIGHT;
        
        setListHeight(usableHeight);
    }, [dataInList]);
    
    

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

    【讨论】:

      【解决方案3】:

      我可以通过在 Autosizer 中禁用高度并像这样手动计算它来解决这个问题:

      const ROW_HEIGHT = 40
      const MAX_HEIGHT = 500
      
      <AutoSizer disableHeight>
        {({ width }) => (
          <List
            width={width}
            height={Math.min(MAX_HEIGHT, ROW_HEIGHT * data.length)}
            rowCount={data.length}
            rowHeight={ROW_HEIGHT}
            rowRenderer={rowRenderer}
            overscanRowCount={10}
          />
        )}
      </AutoSizer>
      

      【讨论】:

        猜你喜欢
        • 2021-06-17
        • 2021-03-10
        • 2018-02-20
        • 2017-04-20
        • 2021-10-28
        • 1970-01-01
        • 2019-06-16
        • 2021-01-18
        • 1970-01-01
        相关资源
        最近更新 更多