Skip to content
Reeflow
Start Building

React Hooks

The Reeflow React SDK provides the useReeflowComponent hook to access component state, data, and control methods without managing refs or event listeners manually.

The main hook that provides comprehensive access to a Reeflow component’s state, data, errors, and control methods.

function useReeflowComponent(componentId: string): {
state: 'initializing' | 'loading' | 'ready' | 'error' | 'empty' | undefined;
data: QueryResult | undefined;
error: ApiError | undefined;
isLoading: boolean;
isReady: boolean;
isError: boolean;
isEmpty: boolean;
refresh: () => void;
reset: (includeQuery?: boolean) => void;
};
  • componentId - The unique ID of the Reeflow component (must match the component’s id prop)

State & Data:

  • state - Current component state: 'initializing', 'loading', 'ready', 'error', or 'empty'
  • data - Query result data when available
  • error - Error object if an error occurred

Convenience Booleans:

  • isLoading - true when state is 'loading'
  • isReady - true when state is 'ready'
  • isError - true when state is 'error'
  • isEmpty - true when state is 'empty'

Control Methods:

  • refresh() - Re-execute the current query to refresh data
  • reset(includeQuery?) - Reset component to initial state (optionally reset query too)
import { ReeflowBarChart, useReeflowComponent } from '@reeflow/react';
function SalesChart() {
const { data, isLoading, isError, error } = useReeflowComponent('sales-chart');
return (
<div>
{isLoading && <Spinner />}
{isError && <ErrorMessage error={error} />}
{data && <p>Loaded {data.row_count} rows</p>}
<ReeflowBarChart id="sales-chart" query={query} />
</div>
);
}

Always provide user feedback for loading and error states:

import { ReeflowBarChart, useReeflowComponent } from '@reeflow/react';
function RevenueChart() {
const { isLoading, isError, error, data } = useReeflowComponent('revenue-chart');
return (
<div>
{isLoading && (
<div className="loading-banner">
<Spinner />
Loading chart data...
</div>
)}
{isError && (
<div className="error-banner">
<strong>Error:</strong> {error?.message}
</div>
)}
{data && (
<div className="stats-badge">
{data.row_count} rows • {data.execution_time_ms}ms
</div>
)}
<ReeflowBarChart
id="revenue-chart"
title="Monthly Revenue"
query={{
type: 'aggregate',
query: {
measures: [{ column: 'revenue', type: 'sum' }],
dimensions: [{ column: 'month', type: 'time', granularity: 'month' }],
},
}}
/>
</div>
);
}

Control your component programmatically with refresh() and reset():

import { ReeflowBarChart, useReeflowComponent } from '@reeflow/react';
function InteractiveChart() {
const { data, refresh, reset, isLoading } = useReeflowComponent('chart');
return (
<div>
<div className="chart-controls">
<button onClick={refresh} disabled={isLoading}>
<RefreshIcon /> Refresh Data
</button>
<button onClick={() => reset()} disabled={isLoading}>
<ResetIcon /> Reset State
</button>
<button onClick={() => reset(true)} disabled={isLoading}>
<ResetIcon /> Reset Query & State
</button>
</div>
{data && <p className="stats">Last updated: {new Date().toLocaleTimeString()}</p>}
<ReeflowBarChart id="chart" query={query} />
</div>
);
}

Coordinate state across multiple charts for a unified loading experience:

import { ReeflowBarChart, ReeflowLineChart, useReeflowComponent } from '@reeflow/react';
function Dashboard() {
const revenue = useReeflowComponent('revenue-chart');
const orders = useReeflowComponent('orders-chart');
const customers = useReeflowComponent('customers-chart');
const isLoading = revenue.isLoading || orders.isLoading || customers.isLoading;
const hasErrors = revenue.isError || orders.isError || customers.isError;
const isReady = revenue.isReady && orders.isReady && customers.isReady;
const refreshAll = () => {
revenue.refresh();
orders.refresh();
customers.refresh();
};
return (
<div className="dashboard">
<div className="dashboard-header">
<h1>Sales Dashboard</h1>
<button onClick={refreshAll} disabled={isLoading}>
Refresh All
</button>
{isLoading && <span className="loading-badge">Loading...</span>}
{hasErrors && <span className="error-badge">Some charts failed</span>}
{isReady && <span className="success-badge">All data loaded</span>}
</div>
<div className="charts-grid">
<div className="chart-card">
<ReeflowLineChart id="revenue-chart" title="Revenue Trend" query={revenueQuery} />
{revenue.data && <p className="chart-meta">{revenue.data.row_count} data points</p>}
</div>
<div className="chart-card">
<ReeflowBarChart id="orders-chart" title="Orders by Category" query={ordersQuery} />
{orders.data && <p className="chart-meta">{orders.data.row_count} categories</p>}
</div>
<div className="chart-card">
<ReeflowLineChart id="customers-chart" title="New Customers" query={customersQuery} />
{customers.data && <p className="chart-meta">{customers.data.row_count} months</p>}
</div>
</div>
</div>
);
}

Use the hook to compute derived statistics from query results:

import { ReeflowBarChart, useReeflowComponent } from '@reeflow/react';
function SalesAnalysis() {
const { data, isReady } = useReeflowComponent('sales-chart');
// Compute derived statistics
const stats = data?.rows
? {
total: data.rows.reduce((sum, row) => sum + (row.revenue || 0), 0),
average: data.rows.reduce((sum, row) => sum + (row.revenue || 0), 0) / data.rows.length,
max: Math.max(...data.rows.map((row) => row.revenue || 0)),
min: Math.min(...data.rows.map((row) => row.revenue || 0)),
}
: null;
return (
<div>
{isReady && stats && (
<div className="stats-panel">
<div className="stat">
<label>Total Revenue</label>
<value>${stats.total.toLocaleString()}</value>
</div>
<div className="stat">
<label>Average</label>
<value>${stats.average.toLocaleString()}</value>
</div>
<div className="stat">
<label>Highest</label>
<value>${stats.max.toLocaleString()}</value>
</div>
<div className="stat">
<label>Lowest</label>
<value>${stats.min.toLocaleString()}</value>
</div>
</div>
)}
<ReeflowBarChart id="sales-chart" title="Revenue by Product" query={query} />
</div>
);
}

Each Reeflow component instance must have a unique ID:

// ✅ Good - unique IDs
<ReeflowBarChart id="revenue-chart" ... />
<ReeflowBarChart id="orders-chart" ... />
// ❌ Bad - duplicate IDs
<ReeflowBarChart id="chart" ... />
<ReeflowBarChart id="chart" ... />

Provide feedback to improve user experience:

function MyChart() {
const { isLoading, isError, error, data } = useReeflowComponent('my-chart');
return (
<div>
{isLoading && <LoadingSpinner />}
{isError && <ErrorBanner error={error} />}
{data && <SuccessBadge count={data.row_count} />}
<ReeflowBarChart id="my-chart" ... />
</div>
);
}

You don’t need useMemo() for query objects. The SDK automatically performs deep equality checks:

// ✅ This works perfectly - no useMemo needed!
<ReeflowBarChart
id="chart"
query={{
type: 'aggregate',
query: {
measures: [{ column: 'revenue', type: 'sum' }],
},
}}
/>;

You can still use useMemo() if the query depends on props or state variables:

const query = useMemo(
() => ({
type: 'aggregate',
query: {
measures: [{ column: selectedField, type: 'sum' }],
},
}),
[selectedField], // Re-create only when selectedField changes
);

All hooks are fully typed. Import types from @reeflow/core:

import type { QueryResult, ApiError } from '@reeflow/core';
import { useReeflowComponent } from '@reeflow/react';
function MyChart() {
const { data, error }: {
data: QueryResult | undefined;
error: ApiError | undefined;
} = useReeflowComponent('my-chart');
// TypeScript knows the exact types
if (data) {
console.log(data.row_count); // ✅ Type-safe
console.log(data.execution_time_ms); // ✅ Type-safe
}
if (error) {
console.log(error.code); // ✅ Type-safe
console.log(error.message); // ✅ Type-safe
}
return <ReeflowBarChart id="my-chart" ... />;
}

All Reeflow React components automatically include event handler props for both standard events and component-specific events:

Standard Events (available on all components):

<ReeflowBarChart
id="chart"
query={query}
onStateChange={(event) => console.log('State changed:', event.detail)}
onDataLoaded={(event) => console.log('Data loaded:', event.detail)}
onError={(event) => console.error('Error:', event.detail)}
/>;

Component-Specific Events:

// Chart components
<ReeflowBarChart
id="chart"
query={query}
onChartClick={(event) => console.log('Chart clicked:', event.detail)}
/>
// Table component
<ReeflowTable
id="table"
query={query}
onTableRowClick={(event) => console.log('Row clicked:', event.detail)}
onTableSortChange={(event) => console.log('Sort changed:', event.detail)}
onTablePageChange={(event) => console.log('Page changed:', event.detail)}
onTableSearchChange={(event) => console.log('Search changed:', event.detail)}
/>

These event handlers are auto-generated from the Core SDK’s custom-elements.json manifest using @fires JSDoc annotations.

See Chart Components and Table Component for complete event details.