如何在窗口调整大小时更新useRef挂钩的值

时间:2019-11-17 18:30:11

标签: javascript reactjs react-hooks ref

我有一个表格组件,该组件在水平滚动时具有固定的第一列。我通过绝对定位来做到这一点。这会引起问题。

调整窗口大小时,表格单元格的内容可能会包裹并导致单元格的高度也随之调整。我已经为表格组件设置了一个ref,以便可以通过javascript获取行高并相应地调整绝对定位的固定单元格的大小。

代码如下:

  const NewStatsTable = ({ headers, stats, data, dataType }) => {
  const [cellHeights, setCellHeights] = useState([])
  const tableRef = useRef(null)

  useLayoutEffect(() => {
    handleCellHeightResize()
    window.addEventListener('resize', handleCellHeightResize)
    return window.removeEventListener('resize', handleCellHeightResize)
  }, [])

  const headersToUse = getHeaders(dataType)

  const getData = (item, header) => {
    if (header === 'gameDate') return formatDate(item[headersToUse[header].id])
    return item[headersToUse[header].id]
  }

  const getTallestCellHeights = () => {
    const rows = Array.from(tableRef.current.getElementsByTagName('tr'))

    return rows.map(row => {
      const fixedCell = row.childNodes[0]
      return Math.max(row.clientHeight, fixedCell.clientHeight)
    })
  }

  const handleCellHeightResize = () => {
    setCellHeights(getTallestCellHeights)
  }

  const headerMarkup = () => (
    <TableRow header>{headers.map(renderHeaderRow)}</TableRow>
  )

  const renderHeaderRow = (header, colIndex) => {
    const text = headersToUse[header].headerText
    const height = cellHeights[0]

    return (
      <TCell
        key={header}
        type='th'
        data={text}
        colIndex={colIndex}
        cellHeight={height}
      />
    )
  }

  const cellMarkup = () =>
    data.map((row, rowIndex) => (
      <TableRow key={row._id}>
        {headers.map((header, colIndex) =>
          renderRow(header, row, rowIndex, colIndex)
        )}
      </TableRow>
    ))

  const renderRow = (header, row, rowIndex, colIndex) => {
    const text = getData(row, header)
    const height = cellHeights[rowIndex + 1]

    return (
      <TCell
        key={header}
        type='td'
        data={text}
        colIndex={colIndex}
        cellHeight={height}
      />
    )
  }

  return (
    <Container>
      <ScrollContainer>
        <Table ref={tableRef}>
          <TableHead>{headerMarkup()}</TableHead>
          <TableBody>{cellMarkup()}</TableBody>
        </Table>
      </ScrollContainer>
    </Container>
  )
}

该代码有效,但在调整大小时不起作用,仅在首次加载页面时有效。如果窗口足够窄,则可以正确计算较高的像元高度。当然,当窗口很宽且单元格中没有文字换行时,情况也是如此。

当我调整窗口大小时,不会重新计算行高。我想这是因为tableRef是在页面加载时创建的,即使页面已调整大小也不会创建。

我尝试为resize事件添加事件侦听器,但这无济于事。 getTallestCellHeights仍使用旧的ref进行计算。

如何更新tableRef,以使getTallestCellHeights使用正确的高度进行计算?

1 个答案:

答案 0 :(得分:0)

我认为这里的问题是高度的计算。您使用的是clientHeight,它不包括边距。在调整页面大小时,计算的高度在变化,并且可能会有一些媒体查询会更新页边距。

useRef可能会按预期工作,但是您的计算并未考虑元素高度的所有值。

请考虑以下代码段中的功能:

  function calcHeight(el) {
      const styles = window.getComputedStyle(el);
      const margin =
        parseFloat(styles["marginTop"]) + parseFloat(styles["marginBottom"]);

      console.log(`trueHeight: ${Math.ceil(el.offsetHeight + margin)}`, `clientHeight: ${el.clientHeight}`);

      return Math.ceil(el.offsetHeight + margin);
    }

您会看到实际的高度有所不同。

function calcHeight(el) {
  const styles = window.getComputedStyle(el);
  const margin = parseFloat(styles["marginTop"]) + parseFloat(styles["marginBottom"]);

  console.log(`trueHeight: ${Math.ceil(el.offsetHeight + margin)}`, `clientHeight: ${el.clientHeight}`);

  return Math.ceil(el.offsetHeight + margin);
}

const demo = document.querySelector('.demo');

function onResize(e) {
  calcHeight(demo);
}

window.addEventListener("resize", onResize);

onResize(demo);
.container {
  display: flex;
  justify-content: center;
  align-items: center;
  background: black;
  width: 100vw;
  height: 100vh;
}

.demo {
  background: red;
  padding: 25px;
  margin: 25px;
  border: 1px solid #fff;
}

@media (min-width: 500px) {
  .demo {
    margin: 50px;
    background: green;
  }
}
<div class="container">
  <div class="demo">
    <h1>Some Content</h1>
  </div>
</div>

Basic React Demo on codeandsandbox