0

working on a React project currently, using webpack, webpack-dev-server, hot module reloading, React Router, styled-components etc

I have created a Table component where I try to dynamically determine the number of rows to render within the Table based on the height of the parent. Within Chrome, this works as expected, but in Firefox I am finding that all my rows are being rendered and therefore are not bound within the parent component.

Is this a known issue or are there any suggested workarounds or is there something horrendously wrong with my code?

Parent component (App):

const MainContainer = styled.div`
  background-color: ${colours.white};
  border-radius: 4px;
  box-shadow: 0 0 9px 0 #dedede;
  display: flex;
  flex-wrap: wrap;
  height: 85%;
  width: 92.5%;
`;


const InnerContainer = styled.div`
  display: flex;
  flex-direction: column;
  width: calc(100% - 9.375em);
`;

const SearchAndCountPlaceholder = styled.div`
  height: 12.5%;
`;

const SidebarPlaceholder = styled.div`
  background-color: ${colours.blue.light};
  height: 100%;
  opacity: 0.12;
  width: 9.375em;
`;

const LoadMoreButtonContainerPlaceholder = styled.div`
  height: 15%;
`;

const App = () => (
  <MainContainer className="app-component">
    <SidebarPlaceholder />
    <InnerContainer>
      <SearchAndCountPlaceholder />
      <Table tableHeadings={tableHeadings} masterData={mockData} />
      <LoadMoreButtonContainerPlaceholder />
    </InnerContainer>
  </MainContainer>
);

Table component:

const Container = styled.div`
  background-color: ${colours.white};
  height: 100%;
  overflow-x: scroll;
  padding-bottom: 1em;
  width: 100%;
`;

const StyledTable = styled.table`
  border-bottom: 2px solid ${colours.grey.lighter};
  border-collapse: collapse;
  margin: 1.25em;
  table-layout: fixed;
  width: 100%;

  & thead {
    border-bottom: inherit;
    color: ${colours.grey.lighter};
    font-size: 0.75em;
    font-weight: 700;
    line-height: 1em;
    text-align: left;
    text-transform: uppercase;

    & th {
      padding: 0.75em 1em 0.75em 1em;
      width: 7.25em;
    }

    & th:not(.Source) {
      cursor: pointer;
    }

    & span {
      color: ${colours.blue.dark};
      font-size: 1em;
      font-weight: 300;
      margin-left: 0.313em;
    }
  }

  & tbody {
    color: ${colours.grey.dark};
    font-size: 0.813em;
    line-height: 1.125em;

    & tr {
      border-bottom: 1px solid ${colours.grey.lightest};

      & td {
        padding: 1em;
      }
    }

    & .masterData {
      font-weight: 700;
    }
  }
`;

let numberOfRowsToDisplay;
const calculateNumberOfRowsToDisplay = () => {
  const tableHeight = document.querySelector('.table-component').offsetHeight;
  const rowHeight = 40; // height of row in pixels
  const numberOfRowsNotToIncludeInCalculation = 2; // header & scrollbar
  numberOfRowsToDisplay = Math.floor(
    tableHeight / rowHeight - numberOfRowsNotToIncludeInCalculation
  );
};

class Table extends Component {
  constructor(props) {
    super(props);
    this.state = { columnToSort: '' };
    this.onColumnSortClick = this.onColumnSortClick.bind(this);
  }

  componentDidMount() {
    calculateNumberOfRowsToDisplay();
  }

  onColumnSortClick(event) {
    event.preventDefault();
    const columnToSort = event.target.className;
    this.setState(prevState => {
      if (prevState.columnToSort === columnToSort) {
        return { columnToSort: '' };
      }
      return { columnToSort };
    });
  }

render() {
  const { tableHeadings, masterData } = this.props;
  const { columnToSort } = this.state;

  const upArrow = '⬆';
  const downArrow = '⬇';

  return (
    <Container className="table-component">
      <StyledTable>
        <thead>
          <tr>
            {tableHeadings.map(heading => (
              <th
                className={heading}
                key={heading}
                onClick={this.onColumnSortClick}
              >
                {heading}{' '}
                {heading !== 'Source' ? (
                  <span>
                    {heading === columnToSort ? upArrow : downArrow}
                  </span>
                ) : null}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {masterData &&
            masterData.slice(0, numberOfRowsToDisplay).map(data => {
              const dataKey = uuidv4();
              return (
                <tr className="masterData" key={dataKey}>
                  <td>Master</td>
                    {Object.values(data).map(datum => {
                      const datumKey = uuidv4();
                      return <td key={datumKey}>{datum}</td>;
                    })}
                </tr>
              );
              })}
          </tbody>
        </StyledTable>
      </Container>
    );
  }
}

Thanks in advance!

Mike Hurl
  • 21
  • 4
  • Have you tried using refs instead of accessing the dom directly? https://reactjs.org/docs/refs-and-the-dom.html – Winter Mar 13 '18 at 11:52
  • Thanks for the suggestion. I've had a quick read of the docs and had a quick playaround, but I am not sure this is going to work for me (or I am doing something wrong!) The issue, I think, is that as I am trying to generate a second integer to pass into .slice(), and calling offsetHeight on a ref always seems to return undefined – Mike Hurl Mar 13 '18 at 15:31

1 Answers1

1

Maybe not the answer I was quite looking for, but in the end I have set the dynamic numberOfRowsToDisplay as part of the component's state; which has given me the UI result I was looking for

Mike Hurl
  • 21
  • 4