Reputation: 6964
I have a React component which renders a table of data from its props. The data is wrapped in MobX's observable()
. From time to time, an asynchronous operation (AJAX call) adds a new row to the data source, which should cause a new table row to render. This is not the case.
A minimal example of the component in question is below:
@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);
});
}
}
The prop model
is an observable object. model.contentTypes
is an array of objects. The table renders correctly on the first draw; it's subsequent re-renders which are problematic.
The bound action onThingHappened
works as expected if the await
is removed. To put it differently - model changes which occur before the first await
trigger re-renders as expected.
The runInAction
does not seem to noticably change anything.
However, if I draw model.contentTypes
in the component itself - rather than simply passing it down to a child component (<Table>
in this case), the entire thing works as expected. For example:
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>
)
In this render function, dereferencing the model.contentTypes
array seems to be enough to trigger the re-render in onThingHappened
, even after an await
.
Upvotes: 1
Views: 684
Reputation: 8418
I don't use mobx but after some research:
<Table/>
in observer()
should work ... if <Table/>
render uses the value directly, it can fail if passed to (unwrapped) subcomponent;'Mobx traces access' then probably it's enough to force mobx to react by 'unnecessary' access.
Try sth like:
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}
/>
);
}
Upvotes: 1