jpfollenius
jpfollenius

Reputation: 16612

All table rows re-render after selection changes

I have table of customers and the selected customer is stored in the ViewState. The problem is that all rows re-render when the selection changes which is quite slow. Ideally only the selected row and the row previously selected would re-render, but I did not find out how to achieve this. My structure is identical to the example in the MobX contact list example:

{this.filteredCustomers.map(customer => {
   return (
      <CustomerRow
         key={customer.id}
         customer={customer}                    
         viewState={this.props.store.view}
      />
   )                
})}

and

const CustomerRow = observer((props: CustomerRowProps) => {
   const isSelected = props.viewState.isCustomerSelected(props.customer)
   const rowClass = isSelected ? 'active' : ''

   return (
     <tr className={rowClass}>
       <td>{props.customer.lastName}</td>
       <td>{props.customer.firstName}</td>
     </tr>
   )
})

All rows depend on the value of ViewState.selectedCustomer through the isCustomerSelected method.

Is there another way to structure this that avoids re-rendering all rows?

Upvotes: 12

Views: 2649

Answers (2)

Tholle
Tholle

Reputation: 112787

The reason why all the rows are re-rendered is because props.viewState.isCustomerSelected(props.customer) has to be re-evaluated for each observer component when the used observable change.

One way to get around this is to use a map so that every entry will have a potential checked field of their own, so that only the selected and deselected components have to re-render.

Example (JSBin)

class AppState {
  @observable todos = [
    {
      id: '1',
      title: 'Do something'
    }, 
    {
      id: '2',
      title: 'Do something else'
    },
    {
      id: '3',
      title: 'Do a third thing'
    }
  ]

}

var appState = new AppState();

@observer
class Todos extends React.Component {
  checked = observable.map({});

  changeTodo = (todo) => {
    this.checked.clear();
    this.checked.set(todo.id, true);
  };

  render() {
    return <div>
      <ul>
        { this.props.appState.todos.map((todo) =>
          <Todo 
            todo={todo} 
            key={todo.id}
            checked={this.checked}
            onChange={() => this.changeTodo(todo)} />
        ) }
      </ul>
      <DevTools />
    </div>;
  }
}

@observer
class Todo extends React.Component {
  render() {
    const { todo, checked, onChange } = this.props;
    const isChecked = checked.get(todo.id);
    return <li>
      <input 
        type="checkbox"
        checked={isChecked} 
        onChange={onChange} />
      {todo.title}
    </li>;
  }
}

Upvotes: 3

bennygenel
bennygenel

Reputation: 24660

You can use shouldComponentUpdate to deside wheather component updates or not.

Upvotes: 4

Related Questions