Почему этот компонент-наблюдатель React / Mobx не рендерится, когда его наблюдаемые реквизиты меняются? - PullRequest
0 голосов
/ 28 мая 2019

У меня есть компонент React, который рендерит таблицу данных из своих реквизитов. Данные обернуты в MobX observable(). Время от времени асинхронная операция (вызов AJAX) добавляет новую строку в источник данных, что должно привести к отображению новой строки таблицы. Это не вариант.

Ниже приведен минимальный пример рассматриваемого компонента:

@observer
class MyDashboard extends React.Component {
    render() {
        return (
            <Table
                columns={this.props.columns}
                dataSource={this.props.model.contentTypes}
            />
        );
    }

    @action.bound
    private onThingHappened = async () => {
        const thing = await asyncThing();
        runInAction('onThingHappened', () => {
            this.props.model.contentTypes.push(thing);
        });
    }
}

Опора model является наблюдаемым объектом. model.contentTypes - это массив объектов. Таблица отображается правильно на первом тираже; это повторные рендеры, которые проблематичны.

Связанное действие onThingHappened работает как положено, если await удален. Иными словами, изменения модели, которые происходят до того, как первый триггер await выполнит повторный рендеринг, как и ожидалось.

runInAction, похоже, ничего не меняет.

Однако, если я нарисую model.contentTypes в самом компоненте - вместо того, чтобы просто передать его дочернему компоненту (в данном случае <Table>), все будет работать, как и ожидалось. Например:

    return (
        <React.Fragment>
            <div style={{display: 'none'}}>{this.props.model.contentTypes.length}</div>
            <Table
                columns={this.props.columns}
                dataSource={this.props.model.contentTypes}
            />
        </React.Fragment>
    )

В этой функции рендеринга разыменование массива model.contentTypes кажется достаточным для запуска повторного рендеринга в onThingHappened даже после await.

1 Ответ

0 голосов
/ 29 мая 2019

Я не использую mobx, но после некоторого исследования:

  • упаковка <Table/> в observer() должна работать ... если <Table/> рендер использует значение напрямую, он может потерпеть неудачу, если передан (развернутому) субкомпоненту;

'Mobx traces access' тогда, вероятно, этого достаточно, чтобы mobx отреагировал 'ненужным' доступом.

Попробуйте что-то вроде:

render() {
  const amount = this.props.model.contentTypes.length;
  if( !amount ) return null; // to avoid 'amount unused, no unused ...'

  return (
    <Table
      columns={this.props.columns}
      dataSource={this.props.model.contentTypes}
    />
  );
}
...