Introduction
Graphite Grid Reactivity
Modern front-end libraries like React rely on reactive programming. Graphite Grid leverages this model to manage its state. By integrating with React's standard rendering system, Graphite Grid can share these reactive primitives with other components in your web application.
Reacting to Grid Changes
To set up Graphite Grid, you must use the useGraphiteGrid
function, which
returns a state object reflecting the current grid state. This state object is a
stable reference and is safe to use in effects and as a dependency in hooks like
useEffect
. It provides a real-time view of the data within the grid.
The state object includes an api
property that refers to the Graphite Grid API
attached to it. Components responding to specific grid state changes can use
api.useApiSlice
to re-render.
import { useGraphiteGrid, GraphiteGridDom } from "@1771technologies/graphite-grid-react";
function MyGrid() {
const rowData = [
// Row data here
];
const columnDefinitions = [
// Column definition here
];
// Our grid component
export function MyGrid() {
const state = useGraphiteGrid({
initial: {
columnDefinitions,
rowDataSource: {
kind: "client",
data: rowData,
},
},
});
return <GraphiteGridDom state={state} />;
}
}
While the example above is contained within a single component, Graphite Grid supports the typical React approach of sharing state by moving it to a parent component and then passing it down as props. Therefore, the state graph of an application that includes Graphite Grid would appear as follows:
useApiSlice
Passing the Graphite Grid state object to different components will not trigger
a render. The state reference is stable and does not change. Instead, the
useApiSlice
function can listen to state changes reactively.
Displayed Columns:
- Alpha
- Beta
- Zeta
import type { GraphiteGridState } from "@1771technologies/graphite-grid-react";
import { GraphiteGridDom, useGraphiteGrid } from "@1771technologies/graphite-grid-react";
const columnSetA = [{ id: "Alpha" }, { id: "Beta" }, { id: "Zeta" }];
const columnSetB = [{ id: "Bravo" }, { id: "Echo" }, { id: "Charlie" }];
export function GridReactivity() {
const state = useGraphiteGrid({
initial: {
columnDefinitions: columnSetA,
rowDataSource: {
kind: "client",
data: [{ Alpha: 1, Beta: 2, Zeta: 3, Bravo: 4, Echo: 5, Charlie: 6 }],
},
},
});
return (
<div className="flex flex-col p-2 gap-4">
<div>
<button
variant="success"
onClick={() =>
state.api.setColumnDefinitionsExn(
state.api.getColumnDefinitions()[0] === columnSetA[0] ? columnSetB : columnSetA,
)
}
>
Swap Random Column
</button>
</div>
<GridChild state={state} />
<ChildUsingState state={state} />
</div>
);
}
function GridChild<T>(props: { state: GraphiteGridState<T> }) {
return (
<div height={82}>
<GraphiteGridDom state={props.state} />
</div>
);
}
function ChildUsingState<T>({ state }: { state: GraphiteGridState<T> }) {
const api = state.api;
const columns = api.useApiSlice((s) => s.getColumnDefinitions());
return (
<div className="flex flex-col gap-1">
<h4 className="uppercase font-bold">Displayed Columns: </h4>
<ul className="my-0">
{columns.map((c) => (
<li key={c.id}>{c.id}</li>
))}
</ul>
</div>
);
}
- Start by creating the Graphite Grid state object
- Add an
onclick
event handler to update the current column definitions of the grid to a new set of column definitions - Share the grid state with any components that need to use the state
- Use
useApiSlice
to select the state that will re-render when it changes declaratively
Danger
useApiSlice
is a hook and, as such, must follow the rules of hooks for
React.
useApiSlice
expects a selector function as its first argument. The value
returned from this selector should be stable. Hence, the following will not work:
api.useApiSlice((s) => [...s.getColumnDefinitions()]);
Every time the selector is run, a new array is called, resulting in
re-render and potentially an infinite loop. As a second argument to
useApiSlice
, you may pass an equalityFunc
where more fine-grained equality
is required.