0

I have a table component I've built for my app that leverages mobx and works fine. I want to add in the ability to update objects inside the tables data without reloading the page. The setup currently looks like this (simplified):

export const SmartTable = observer(class SmartTable extends Component {
  dataRows = []
  selectedRows = []

  fetchData = async () => {
    this.loading = true;
    this.dataRows.clear();

    const response = await client.get(this.props.dataUrl, params);

    for (const row of response.data.map(item => new this.props.model(item))) {
      this.dataRows.push(row)
    }

    this.loading = false;
  }

  render() {
    <BootstrapTable
      data={this.dataRows}
      columns={this.dataColumns}
      selectRow={this.props.actions && {
                            mode: 'checkbox',
                            onSelect: (row, isSelect, rowIndex, e) => {
                              if (isSelect) {
                                this.selectedRows.push(row);
                              } else {
                                const filteredObjects = this.selectedRows.filter((r) => r.id !== row.id);
                                this.selectedRows.replace(filteredObjects);
                              }
                            },
                            onSelectAll: (isSelect, rows, e) => {
                              if (isSelect) {
                                this.selectedRows.replace(rows);
                              } else {
                                this.selectedRows.replace([]);
                              }
                            },
                          }}
    />
  }

})

decorate(SmartTable, {
  dataCols: observable,
  dataRows: observable,
  selectedRows: observable,
});

I have models that look like this:

import { action, computed, observable } from "mobx"

export class Earning {
    id = null

    @observable amount
    @observable status

    constructor(data) {
      this.id = data?.id

      this.updateFromJson(data)
    }

    @computed get asJson() {
      return {
        id:               this.id,
        amount:           this.amount,
        status:           this.status,
      }
    }

    @action updateFromJson(data) {
      this.id               = data.id
      this.amount           = data.attributes.amount
      this.status           = data.attributes.status
    }
}

Inside my code, I use the SmartTable and render a button into the column to approve the earning:

export const EarningsTable = observer(class EarningsTable extends Component {
  approveAllEarnings = async (earnings) => {
    if (window.confirm('Are you sure you want to approve all selected earnings?')) {
      await Promise.allSettled(earnings.map(async (earning) => {
        await client.put(`/earnings/${earning.id}/`, { earning: { status: 'approved' } });
        runInAction(() => {earning.status = 'approved'});
      }))
    }
  }

  columns = [
    {
      dataField: 'id',
      text: 'ID',
      formatter: this.idFormatter,
      sort: true,
      headerStyle: () => ({ width: '100px' }),
    },
    {
      dataField: 'amount',
      text: 'Amount',
      formatter: this.amountFormatter,
      sort: true,
    },
    {
      dataField: 'status',
      text: 'Status',
      sort: true,
      formatter: this.statusFormatter,
      headerStyle: () => ({ width: '90px' }),
    },
    {
      dataField: '',
      text: 'Actions',
      formatter: this.approveButton,
    },
  ]

  render() {
    return (
        <SmartTable
          model={Earning}
          dataUrl={this.props.dataUrl}
          columns={this.columns}
          actions={{
            'Approve Checked': earnings => this.approveAllEarnings(earnings),
          }}
        />
    );
  }
});

decorate(EarningsTable, {
  approveAllEarnings: action,
});

When I click the button to "approve" the earning, the Earning model gets updated properly, but the table doesn't update. Is there a way to get this working?

reproducible example: https://codesandbox.io/s/hopeful-forest-hq9kf

Brad Herman
  • 9,665
  • 7
  • 28
  • 30
  • `this.dataRows` might not be picked up by the `SmartTable` observer component as it is not dereferencing the array values, [as discussed in the documentation](https://mobx.js.org/understanding-reactivity.html#incorrect-use-an-observable-but-without-accessing-any-of-its-properties). Does it work if you write ``? – Tholle Dec 15 '20 at 23:28
  • I had tried that but it didn't work either... I'm sure there's something small I'm missing and it's driving me nuts! – Brad Herman Dec 16 '20 at 17:37
  • Just updated the code example to what I'm trying now... I see a re-render in the mobx inspector, but not after the `runInAction` bit... That registers, but does not trigger a re-render of the table. – Brad Herman Dec 16 '20 at 18:03
  • Great. Do you think you could create a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) in e.g. [CodeSandbox](https://codesandbox.io/s/)? Would be easier to see what might be wrong with a running example. – Tholle Dec 16 '20 at 22:53
  • Yep! https://codesandbox.io/s/hopeful-forest-hq9kf – Brad Herman Dec 17 '20 at 20:56
  • Your table component doesn't seem to support controlling the data yourself, it wants to do that internally so it will not re-evaluate an element data unless the length of `data` changes. You also want to use [`makeObservable`](https://mobx.js.org/enabling-decorators.html#enabling-decorators-) on `this` in the `Earning` constructor to make the class instances observable, since you are using MobX 6. – Tholle Dec 17 '20 at 23:52
  • 1
    Ahh yeah I figured it might be doing something to the data... Going to keep looking for a solution to maybe get this working :\. Thanks for your help! – Brad Herman Jan 08 '21 at 03:24

0 Answers0