kaze13
kaze13

Reputation: 309

All TableRows are rerendered each time a row is selected with React and material-ui

I have a code snippet like this

<Table selectable onRowSelection={this.onRecordSelected} bodyStyle={tableBodyStyle}>
        <TableBody deselectOnClickaway={false} showRowHover displayRowCheckbox={false}>
          {this.props.taskRecords.map((row, index) => (
            <TableRow key={row.get('id')} selected={this.state.selectedRecordRowId === index}>
              <TableRowColumn>{row.getIn(['task', 'name'])}</TableRowColumn>
              <TableRowColumn>{row.getIn(['task', 'description'])}</TableRowColumn>
              <TableRowColumn>{row.get('status')}</TableRowColumn>
              <TableRowColumn>{row.get('log')}</TableRowColumn>
            </TableRow>
          ))}
        </TableBody>
</Table>

I checked the source code of TableRow and TableRowColumn, I found they didn't implement shouldComponentUpdate method. I understand they would be rerendered when any state is changed.

I tried to use pure function in recompose library to purify these to components.

const TableRow = pure(require('material-ui/lib/table/table-row'));
const TableRowColumn = pure(require('material-ui/lib/table/table-row-column'));

They were still rerendered enter image description here

In my understanding, components after purified wouldn't be rerendered anymore if given props are not changed. In this case, all props are either Immutable objects from Immutable.js or functions.

Is there any way to prevent it from rerendering when I select any row?

Thank you in advance.

Upvotes: 2

Views: 2858

Answers (2)

kaze13
kaze13

Reputation: 309

Finally I found a solution.

<Table selectable onRowSelection={this.onRecordSelected} bodyStyle={tableBodyStyle}>
        <TableBody deselectOnClickaway={false} showRowHover displayRowCheckbox={false}>
          {this.props.taskRecords.map((row, index) => (
            <PureRecordRow key={row.get('id')} selected={this.state.selectedRecordRowId === index}>
              <TableRowColumn>{row.getIn(['task', 'name'])}</TableRowColumn>
              <TableRowColumn>{row.getIn(['task', 'description'])}</TableRowColumn>
              <TableRowColumn>{row.get('status')}</TableRowColumn>
              <TableRowColumn>{row.get('log')}</TableRowColumn>
            </PureRecordRow>
          ))}
        </TableBody>
 </Table>

I created a new component PureRecordRow which extends the TableRow with custom shouldComponentUpdate method:

class PureRecordRow extends TableRow{
  constructor(props, context){
    super(props, context)
  }
  shouldComponentUpdate(nextProps, nextState) {
    return this.state.hovered !== nextState.hovered || this.props.selected !== nextProps.selected;
  }
}

Upvotes: 2

steppefox
steppefox

Reputation: 1844

In general, yes - render function will be called each time in TableRow and TableRowColumn if their parent component will be rerendered, but it is not so bad, because render function do not change your RealDOM each time, so it is not so bad for your application performance. But, if you really want to prevent that because of reasons, you can for example create new component MyRow and render it instead of TableRow + TableRowColumn.

So, your code will be like:

<Table selectable onRowSelection={this.onRecordSelected} bodyStyle={tableBodyStyle}>
    <TableBody deselectOnClickaway={false} showRowHover displayRowCheckbox={false}>
      {this.props.taskRecords.map((row, index) => (
        <MyRow key={index} row={row} isSelected={this.state.selectedRecordRowId === index} />
      ))}
    </TableBody>
</Table>

And in MyRow component you can implement shouldComponentUpdate (or PureRenderMixin) and render real material-ui components:

render() {
    var isSelected = this.props.isSelected;
    var row = this.props.row;
    return <TableRow key={row.get('id')} selected={isSelected}>
          <TableRowColumn>{row.getIn(['task', 'name'])}</TableRowColumn>
          <TableRowColumn>{row.getIn(['task', 'description'])}</TableRowColumn>
          <TableRowColumn>{row.get('status')}</TableRowColumn>
          <TableRowColumn>{row.get('log')}</TableRowColumn>
    </TableRow>
}

Upvotes: 1

Related Questions