Active_cell and page_current conflict

I am trying to link a data table with multiple pages and a graph that displays all the pages of the data table. When I click on the data table, the relevant vertex in the graph is highlighted. When I hover over a vertex, the page_current value in the data table is updated and the active_cell is changed.

The issue I am having is when I update page_current and active_cell in a callback, active_cell updates, then page_current_updates, which clears the active cell. This results in the active_cell only highlighting if the vertex on the graph is in the same page as the data table. If it isn’t, the active_cell blinks and the page_current changes, with no cell highlighted. If you unhover then hover over the same point, the active cell will highlight.

I created a workaround by creating a chained callback (which is commented out below) named placeholder. With the chained callback, page_current is updated on the data table and the active cell is passed to the placeholder, which then updates the active cell in the data table. This works, but throws the errors below

Circular Dependencies
10:31:41 AM
Error: Dependency Cycle Found: data_table.page_current → placeholder.active_cell → data_table.active_cell → placeholder.active_cell

Is there a way to link a graph and data table with multiple pages without throwing errors by updating the page_current before the active_cell in a single call back or using a method like the chained callbacks I commented out?

from jupyter_dash import JupyterDash
from dash import Dash, dcc, html, Input, Output, no_update, dash_table, State, callback_context
from dash.exceptions import PreventUpdate
import as px
import pandas as pd
import dash_bootstrap_components as dbc
import json
from datetime import datetime as dt, timedelta
import plotly.graph_objects as go
import os, sys
import datetime
import re
import time
import numpy as np
from collections import defaultdict

app = JupyterDash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
dict_list = [{'fid': 'a1', 'tail': 'test1', 'x': 1, 'y': 1}, {'fid': 'b2', 'tail': 'test2', 'x': 2, 'y': 2},
             {'fid': 'c3', 'tail': 'test3', 'x': 3, 'y': 1}, {'fid': 'd4', 'tail': 'test1', 'x': 2, 'y': 2}, {'fid': 'e5', 'tail': 'test2', 'x': 4, 'y': 4},
             {'fid': 'f6', 'tail': 'test3', 'x': 6, 'y': 4}, {'fid': 'g7', 'tail': 'test1', 'x': 3, 'y': 3}, {'fid': 'h8', 'tail': 'test2', 'x': 6, 'y': 6},
             {'fid': 'i9', 'tail': 'test3', 'x':12, 'y': 10}]
df = pd.DataFrame(dict_list)

app.layout = html.Div(
                columns=[{"name": i, "id": i} for i in ['fid', 'tail', 'x', 'y']],
                'if': {'column_id': 'date_processed'},
                'pointer-events': 'None'}]),
            style={'height': 750, 'overflowY': 'scroll'},
            className='six columns'),


operators = [['ge ', '>='],
             ['le ', '<='],
             ['lt ', '<'],
             ['gt ', '>'],
             ['ne ', '!='],
             ['eq ', '='],
             ['contains '],
             ['datestartswith ']]

def split_filter_part(filter_part):
    for operator_type in operators:
        for operator in operator_type:
            if operator in filter_part:
                name_part, value_part = filter_part.split(operator, 1)
                name = name_part[name_part.find('{') + 1: name_part.rfind('}')]

                value_part = value_part.strip()
                v0 = value_part[0]
                if (v0 == value_part[-1] and v0 in ("'", '"', '`')):
                    value = value_part[1: -1].replace('\\' + v0, v0)
                        value = float(value_part)
                    except ValueError:
                        value = value_part

                # word operators need spaces after them in the filter string,
                # but we don't want these later
                return name, operator_type[0].strip(), value

    return [None] * 3

    Output('data_table', "data"),
    Output('graph', "figure"),
    Output("graph-tooltip", "show"),
    Output("graph-tooltip", "bbox"),
    Output("graph-tooltip", "children"),
    #Output("memory-output", "data"),
    Output("data_table", "active_cell"),
    Output("data_table", "selected_cells"),
    Output('data_table', 'page_current'),
    Output('tbl_out', 'children'),
    Input('data_table', "page_current"),
    Input('data_table', "sort_by"),
    Input('data_table', "filter_query"),
    Input("graph", "hoverData"),
    Input("graph", "figure"),
    Input('data_table', 'active_cell'),
    State('data_table', 'data'),
    State('data_table', "page_size"))
def update_table(page_current, sort_by, filter,  hoverData, dict_fig, active_cell, data_table, page_size):
    dff = get_table(df, page_size, page_current, sort_by, filter)
    triggered_id = callback_context.triggered[0]['prop_id']
    if 'data_table.active_cell' == triggered_id:
            active_cell, selected_cells, fig = update_graphs(active_cell, data_table, dict_fig)
            return no_update, fig, False, no_update, no_update, active_cell, selected_cells, no_update, F"{callback_context.triggered}-----{callback_context.triggered_prop_ids}---{callback_context.triggered_id}"
        except Exception as e:
            return no_update, no_update,  no_update, no_update, no_update, no_update, no_update, no_update, str(e)
    elif 'graph.hoverData' == triggered_id:
            #show, bbox, children, active_cell, selected_cells, geojson3, idx, error = update_tooltip_content(hoverData, flights_geojson, flight_data, dff, page_size)
                show, bbox, children, active_cell, selected_cells, idx, error = update_tooltip_content(hoverData, data_table, dff, page_size)
                hover_rec = hoverData['points'][0]['customdata'][0]      
                hover_rec = None
            if hover_rec is not None:
                fig = go.Figure(dict_fig)
                fig.for_each_trace(lambda trace: trace.update(visible=False) if == "highlight" else())
                page_current = int((idx)/page_size)
                return dff.iloc[
            page_current*page_size: (page_current + 1)*page_size
        ].to_dict('records'), fig, show, bbox, children,active_cell, selected_cells, page_current, page_current
                return no_update, no_update,  False, bbox, no_update,no_update, no_update, no_update,no_update#F"{idx}:{active_cell}"
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            return no_update, no_update,  no_update, no_update, no_update, no_update, no_update, no_update,F"{exc_type}, {fname}, {exc_tb.tb_lineno}"

        fig = px.line(dff.sort_values('x').reset_index(drop=True)[['tail', 'fid', 'x', 'y']], x="x", y="y",color="tail",hover_data=[dff.fid])

        fig.add_scatter(y=[0], x=[0], mode="markers", visible = False, name = 'highlight', hoverinfo='skip', showlegend=False, marker=dict(size=12))

        return dff.iloc[
            page_current*page_size: (page_current + 1)*page_size
        ].to_dict('records'), fig, no_update, no_update, no_update,no_update, no_update, page_current, no_update

#    Output("data_table", "active_cell"),
#    Input('memory-output', "data"))
def update_highlight(active_cell):
    return active_cell
def get_table(dff, page_size, page_current, sort_by, filter):
    if filter is not None:
        filtering_expressions = filter.split(' && ')

        for filter_part in filtering_expressions:
            col_name, operator, filter_value = split_filter_part(filter_part)
            if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
                # these operators match pandas series operator method names
                dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
            elif operator == 'contains':
                dff = dff.loc[dff[col_name].str.contains(filter_value)]
            elif operator == 'datestartswith':
                # this is a simplification of the front-end filtering logic,
                # only works with complete fields in standard format
                dff = dff.loc[dff[col_name].str.startswith(filter_value)]

    if len(sort_by):
        dff = dff.sort_values(
            [col['column_id'] for col in sort_by],
                col['direction'] == 'asc'
                for col in sort_by
    return dff.reset_index(drop=True)

def find_xy(fid, json_dict):
    for tail_obj in json_dict:
        i = 0
        for obj in tail_obj['customdata']:
            if obj[0] == fid:
                return tail_obj['x'][i], tail_obj['y'][i]
            i +=1
    return None, None
def find_table_idx(fid, flight_data):
    i = 0
    for row_obj in flight_data:
        if fid == row_obj['fid']:
            return i
        i += 1
    return None

def update_tooltip_content(hoverData, flight_data, dff, page_size):
        if hoverData is None:
            return no_update, no_update, no_update, no_update, no_update, None, None
        pt = hoverData["points"][0]
        bbox = pt["bbox"]
        fid = hoverData['points'][0]['customdata'][0]
            idx = dff.index[dff['fid'] == fid][0]
            activecell, selectedcells = [{'column':0, 'row':idx-int(idx/page_size)*page_size, 'column_id': 'noupdate'}, [{'column':0, 'row':idx-int(idx/page_size)*page_size}]]
            idx = 0
            activecell, selectedcells = [None, []]
        children = [
            html.P(f"Date: {pt['x']}, ASP: {pt['y']}, fid: {fid}")
        return True, bbox, children, activecell, selectedcells, idx, F"{idx}:{fid}:{dff.index[dff['fid'] == fid][0]}-----{dff.to_json(orient='records')}"
    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        return no_update, no_update, no_update, no_update, no_update, 0, F"{dff.to_json(orient='records')}----{idx}:{fid}----{exc_type}, {fname}, {exc_tb.tb_lineno}"

def update_graphs(active_cell, flight_data, dict_fig):
    if active_cell:
        fig = go.Figure(dict_fig)
            lambda trace: trace.update(visible=False) if == "highlight" else (),
        if active_cell['column_id'] =='fid':
            fid = flight_data[active_cell['row']]['fid']
            x, y = find_xy(fid, dict_fig['data'])

            if x is not None and y is not None:
                    lambda trace: trace.update(visible=True, x=[x], y=[y]) if == "highlight" else (),
            return active_cell, [active_cell], fig
        elif active_cell['column_id'] =='noupdate':
            return active_cell, [active_cell], no_update
            return None, [], fig
        return None, [], no_update
if __name__ == '__main__':
    app.run_server(debug=True) # Running the application```