Tokyo D
Tokyo D

Reputation: 233

Update individual DataTable cells without re-rendering entire table

I have a DataTable which I want to update with stock market prices (using the interval callback). The table is quite large, so I don’t want to re-render the entire table every time a single price changes. Is it possible to update just the cell containing the particular stock price which has changed? Basically, I want the price cells updating quickly and smoothly without everything else reloading. Is this possible using regular Dash components, or would I need to create my own table using React and handle the price updates on the React side? Any ideas welcome. Thanks!

Upvotes: 0

Views: 612

Answers (1)

EricLavault
EricLavault

Reputation: 16095

The DataTable component provides pagination precisely for the purpose of not having to render all the data on the screen, which by default is enabled and set to 250 rows per page.

Pagination is used to improve performance: instead of rendering all of the rows at once (which can be expensive), we only display a subset of them. With pagination, we can either page through data that exists in the table (e.g. page through 10,000 rows in data 100 rows at a time) or we can update the data on-the-fly with callbacks when the user clicks on the "Previous" or "Next" buttons. These modes can be toggled with the page_action parameter.

The best option imho is to take advantage of that and let the frontend do the work accordingly. The plus is that, when fetching stock market prices there could only be from zero to 250 (or <page_size>) rows to update on the screen, so in most cases (with a reasonable page size) it guarantees quick and smooth transitions.

The parameters are :

  • page_action (default: 'native')
  • page_size (default: 250)
  • page_current (default: 0)
  • page_count (optional)

Another idea (which does not exclude to use pagination) would be to make the interval callback set data updates to a dcc.Store component instead of updating the table directly. Data from the store can then be read clientside which allows to handle cell updates manually using javascript.

For example, sending current time as data :

from datetime import datetime as dt

@app.callback(
    Output('store-id', 'data'),
    Input('interval-id', 'n_intervals')
)
def refresh_data(n_intervals):
    data = {'time': dt.now().strftime('%H:%M:%S')}
    return data

Then you can also register a clientside callback to read these data from the client and do the updates when triggered :

app.clientside_callback(
    '''
    function(data) {
        // data is a JSON object. 
        console.log("callback receives", data);

        // Here you can either update the Output component the 
        // Dash way (<output> is the DataTable component id) :
        // return data;

        // Or if you need more freedom, you would use a dummy output 
        // (all Dash callbacks must have at least one output) and 
        // tell Dash to not update it, for example : 
        updateTable(data);
        return window.dash_clientside.no_update;
    }
    ''',
    Output('<output>', '<property>'),
    [Input('store-id', 'data')]
)

Also depending on the storage_type : for local and session you can always use localStorage and sessionStorage to get the data as a JSON string, eg.

data = JSON.parse(localStorage.getItem('store-id'));

With storage_type='memory' it seems there is no way to get the data outside of a client side callback (though I haven't searched further).

Upvotes: 1

Related Questions